Search code examples
blockchainethereumsolidity

Difference between a state array initialized using literals and another state array initialized using the "new" operator in Solidity?


Considering the contract below, what is the difference between: a) arr_1 and arr_2 b) arr_3 and arr_4

contract Dummy{
    uint8[] public arr_1 = new uint8[](4);
    uint8[] public arr_2 = [0,0,0,0];
 
    function f() public pure {
        uint[3] memory arr_3;
        uint[] memory arr_4 = new uint[](3);
    }
}

Also, Solidity says that we can create local memory dynamic arrays using the new operator, like below:

 function f() public pure {
        uint[] memory arr = new uint[](3);
        //The following lines give error        
        //arr.push(10); //ERROR: local memory arrays dont support PUSH
        //arr.pop(); //ERROR: local memory arrays dont support POP
}

However, they don't support push and pop. So, how exactly are they dynamic?

The Solidity version I am referring to is 0.8.13

EDIT Adding one more example, this time with gas costs

//Deplyment Cost: 171321
contract CostTest_0 {
    uint8[] public arr_0 = [0,0,0];
    
}

//Deplyment Cost: 172504
contract CostTest_1 {
    uint8[] public arr_1 = new uint8[](3);  //This is another way.   
}

//Deplyment Cost: 417617
contract CostTest_2 {
    uint8[] public arr_0 = [0,0,0];     //One way of declaring dynamic arrays
    uint8[] public arr_1 = new uint8[](3);  //This is another way.
    
    //31443
    function f() public {
        arr_0.push(100);
    }

    //31553    
    function g() public {
        arr_1.push(100);
    }
    //h(0) => 26188, h(1) => 26250
    function h(uint256 idx) public view returns (uint8) {
        return arr_0[idx];
    }
    //i(0) => 26144, i(1) => 26206
    function i(uint256 idx) public view returns (uint8) {
        return arr_1[idx];
    }


    //p(0) => 22030 , p(1) => 22042
    function p(uint256 idx) public pure returns (uint8) {
        uint8[] memory tmp = new uint8[](3);
        return tmp[idx];
    }
    //q(0) => 22009 , q(1) => 22021
    function q(uint256 idx) public pure returns (uint8) {
        uint8[3] memory tmp = [0, 0, 0];  
        return tmp[idx];
    }
}

Solution

  • Regarding differences between arr1, arr2, arr3 and arr4;

    arr1 and arr2 are state variables that are kept in storage, which means, they will exists even though transaction is finished executing

    However, arr3 and arr4 are local memory arrays, that are cleared after execution.

    There are huge gas differences between (1,2) and (3,4). Storage is much more expensive to use.

    Any operation with a memory array will use 3 gas, (mstore and mload), While loading from storage uses 200 and writing to it uses at least 5000 (sload and sstore)

    arr2 will use slightly less gas to deploy compared to arr1 since you are saving some computations and therefore a smaller bytecode. While using them, there should be no difference. This applies to arr3 and arr4 as well.

    Using the variation with the new keyword, solidity allows you to decide the size in runtime, and this support comes naturally with some more gas costs given more computations. For example,

    contract A {
        uint256 arrSize;
        constructor(uint256 _size){
            arrSize = _size;
        }
    
        function giveMeAnArray() public pure returns (uint256[]){
            uint256[arrSize] memory arr; // this is not allowed
            uint256[] memory arr = new uint256[](arrSize); // but this is allowed
        }
    }