17

Is there a mistake in the way I initialize Bar b in the function foobar?

contract Foo {

    struct Bar {
        address owner;
        uint[] x;
    }

    Bar[] public bars;

    function foobar(address a) public {
        Bar storage b = Bar(a, new uint[]) // will that be an array in storage?
        bars.push(b)
    }

}
Manan Mehta
  • 350
  • 1
  • 2
  • 9

3 Answers3

24

There is no need to initialize storage arrays in Solidity. Only memory arrays has to be initialized before usage.

So in your case, no need to initialize x inside Bar as long as you are not assigning a value to one of the x indexes inside your foobar. Actually, making initialization in your code will consume gas for no reason.

The following code works well for your case:

function foobar(address a) public {
    Bar memory b;
    b.owner = a;
    //When 'b' is pushed to 'bars' array:
    // (1) 'b' will be converted from memory to storage.
    // (2) And 'x' inside it will be initialized automatically.
    bars.push(b); 
}

However, if you need to access x and push some value you can use push:

function foobar2(address a, uint x0) public {
    Bar memory b;
    b.owner = a;
    bars.push(b);
    //Trying: b.x[0] = x0; will generate error. Since, b.x is a memory array that is not initialized!
    bars[bars.length - 1].x.push(x0); //This will work fine!
}

Actually, you can still initialize your memory array, as an empty one, as follow:

function foobar3(address a) public {
    Bar memory b = Bar(a, new uint[](0)); //Thanks to "James Duffy" for his answer.
    bars.push(b);
}

Last thing to mention is that: If you have multi values, that you need to insert to your x array, then you can do this as follow:

function foobar4(address a, uint[] _x) public {
    Bar memory b = Bar(a, _x);
    bars.push(b);
}

Or as follow. But, this will consume more gas:

function foobar5(address a, uint[] _x) public {
    Bar memory b;
    b.owner = a;
    bars.push(b);

    Bar storage c = bars[bars.length - 1]; // Get the newly added instance of the storage struct
    for (uint i = 0; i < _x.length; i++) {
        c.x.push(_x[i]);
  }

Check full code at the nice EthFiddler: https://ethfiddle.com/fQI6Khgz3E

Muhammad Altabba
  • 2,157
  • 1
  • 15
  • 31
  • Welcome. And thanks to Georgios Konstantopoulos and Loom Network: https://medium.com/loom-network/ethereum-solidity-memory-vs-storage-how-to-initialize-an-array-inside-a-struct-184baf6aa2eb – Muhammad Altabba Feb 20 '18 at 12:01
  • After calling foobar, foobar2 or foobar3, I get value of bars[0] == {owner: address} without property x at all!!! It's completely ignored. How is it possible? What am I doing wrong? – Artod Mar 08 '18 at 17:06
  • 1
    I figured out why this is happening. Need to have separate getter in order to receive arrays from structs like getXFromBar – Artod Mar 08 '18 at 17:45
3

Working fiddle:

https://ethfiddle.com/Yn6fLiAjto

One way to make this work is to create your temporary Bar in memory before pushing it to bars (Bar memory b instead of Bar storage b). Solidity will then automatically copy the struct data from memory to storage. See: https://ethereum.stackexchange.com/a/4476/22415 for more details.

Also new uint[] is a function which requires a size argument, e.g. new uint[](8).

So in full, that line should read Bar memory b = Bar(a, new uint[](8));

James Duffy
  • 321
  • 2
  • 5
1

Try this:

struct Address {
    address[] addressList;
}

function createProposal() public { address[] memory emptyAddressList; Address memory _address = Address({ addressList: emptyAddressList }) }

Ry Zun
  • 11
  • 1