Shariq Hasan Khan
Shariq Hasan Khan

Reputation: 530

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];
    }
}

Upvotes: 0

Views: 360

Answers (2)

keser
keser

Reputation: 2642

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
    }
}

Upvotes: 1

keser
keser

Reputation: 2642

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

  1. Solidity doesn't support dynamic arrays in memory, it is only allowed in storage
  2. The array in your code sample is not a dynamic array. You are creating a memory with a preallocated memory / array length.

So long story short:

No push/pop because they aren't really dynamic arrays in common sense

So I guess solidity documentation might be a little confusing regarding this. Imagine 3 different scenerios

  • A static array, that you specify the length compile time. You can not resize it afterwards, and you need to know its size.
    uint[] memory arr = new uint[](4);
  • The so called -static array with dynamic size-. You don't have to know it's exact size compile time, which means you can allocate the memory for array dynamically? in runtime. But this will still be a regular array, that takes n consecutive memory slots in the memory. So no pop or push methods that resize the array
    uint[] memory arr = new uint[](maxNumberOfTokens);
  • A really dynamic array. For solidity, it is only possible in storage. You can resize it anyway you want. To see how solidity achieves dynamic arrays, you can take a look at Layout in storage, But in short; you take the keccak256 hash of the array's first slot that keeps it's length, and the array starts from the slot that corresponds to it's hash. So that we won't experience collisions

Upvotes: 1

Related Questions