2

When I use the below contract in solidity.

struct Funder {
    address addr;
    uint amount;
}

contract FundContract { struct Campaign { uint numFunders; mapping (uint => Funder) funders; } mapping (uint => Campaign) campaigns;

function updatestruct(uint campaignID) public
{
      Campaign storage c = campaigns[campaignID];

      c.funders[c.numFunders++] = Funder(msg.sender, 100);  //  How this is fine ?

      Funder storage f  = Funder(msg.sender, 100); // Why this is a compilation error
}

}

As per the solidity documentation, the line

 c.funders[c.numFunders++] = Funder(msg.sender, 100);

creates a new temporary memory struct, initialized with the given values, and copies it over to local storage c.

Similarly, I tried the below line

Funder storage f  = Funder(msg.sender, 100);

I was expecting the same, however, I get the compilation error instead " Type struct Funder memory is not implicitly convertible to expected type struct Funder storage pointer."

Why is there a compilation error here, as I expected it to create a temp memory and copy it over to the local storage f like the previous one with local storage c?

Are some details available as to how solidity struct memory works in both cases?

yk28
  • 25
  • 1
  • 4
  • I think this answer will be helpful: https://ethereum.stackexchange.com/questions/72307/type-string-memory-is-not-implicitly-convertible-to-expected-type-struct-insanfa – Julissa DC Oct 15 '21 at 18:37
  • Thanks @JulissaDC, but nope I don't think it fits this category as maps in solidity have only storage. The link you shared does not have local storage and does not explain why temp memory was not created for local storage. – yk28 Oct 16 '21 at 02:25

1 Answers1

2

When you declare a mapping inside a struct, it can only be of type storage, you can't assign values to it in a temp memory, it just won't work. Here's a link that might help you: using mappings inside structs. When you create a struct Funder and assign it to your mapping funders at a specific index it works because you're accepting a struct of type Funder=>mapping (uint => Funder) funders;However, when you're trying to allocate a struct to a type storage, you have to explicitly do so. For ex:

c.numFunders++;
Funder storage f = c.funders[c.numFunders];
f.addr = msg.sender;
f.amount = 100;

The method you used is for the memory allocation. Here's another link that will explain the process. https://medium.com/loom-network/ethereum-solidity-memory-vs-storage-how-to-initialize-an-array-inside-a-struct-184baf6aa2eb. An array has been used in this example but it's based on the same principle.

Icarus23
  • 351
  • 1
  • 6
  • Exactly, you initialize it and assign it to the specified index in the map. – Icarus23 Oct 16 '21 at 12:54
  • Thanks, @icarus23. So what I understand is: In Solidity, for maps, as it has only storage type, it is possible to create a new temp memory, initialize it, and copy it over to storage if the map was part of a local storage variable (in this case Campaign storage c ).

    However, for the regular structs and arrays in local storage without maps, you cannot create temp memory and copy it to local storage.

    I don't get the idea as to why it was designed like this. For maps temp memory gets created and then copied to storage , while for structs and arrays this does not work.

    – yk28 Oct 16 '21 at 12:57
  • I don't understand exactly what you mean by However, for the regular structs and arrays in local storage without maps, you cannot create temp memory and copy it to local storage. You can achieve the desired result with a regular struct: MemoryStruct memory structGig = MemoryStruct(param,param) and then copy it to the storage by creating an array MemoryStruct[] struct and pushing it into the array struct.push(structGig). Here's a link that explains the process better. https://medium.com/coinmonks/ethereum-solidity-memory-vs-storage-which-to-use-in-local-functions-72b593c3703a – Icarus23 Oct 16 '21 at 13:09
  • I can reorganize my sentence for easy understanding.

    The line,

    c.funders[c.numFunders++] = Funder(msg.sender, 100); 
    

    assignment is for local storage map, Funder is first allocated in temp memory, initialised and then copied to local storage variable c.funders.

    For the line,

     Funder storage f  = Funder(msg.sender, 100);
    

    which is a local storage struct, Funder cannot be allocated in temp memory, initialized and copied to local storage f. In this case, you need explicit storage assignment only.

    – yk28 Oct 16 '21 at 13:20
  • In the first case, it is done automatically by the compiler, it creates temp memory and copies to storage as it involved maps, while in the second case the user has to explicitly do it for structs and arrays. – yk28 Oct 16 '21 at 13:27
  • If you want to use storage for it, yes because with storage it must point to a location. However, if you want to use memory instead of storage for Funder struct, it will work because the struct itself does not have any storage instances in it(array or map). So basically if you do Funder memory f = Funder(msg.sender, 100); it should work. After, you can assign it to the map funders: funders[numFunders] = f. Is this what you were looking for? – Icarus23 Oct 16 '21 at 13:37
  • 1
    Thanks @icarus23. I am not looking for anything specific, I am just trying to understand the solidity compiler behavior for maps, structs, and arrays.

    Now I get the behavior of the compiler. For maps (has only storage), temp memory is automatically created and copied to storage, while for structs and arrays in storage, compiler does not handle it, the user needs to do it explicitly as you said. (either assign to another storage variable or use memory and struct.push()).

    – yk28 Oct 16 '21 at 13:57