4

I'm curious what happens if you try to do the next thing:

contract C {
    Struct S {
        uint a;
        uint b;
    }

    mapping(address => s) structs;

    function updateStructs(S sInstance) private {
        structs[msg.sender] = sInstance;
    }

    function addStruct (uint _a, uint _b) payable {
        S memory s = S({a: _a, b: _b});
        updateStructs(s);
    }
}

As I understood how variables work, the addStruct creates memory variable that has only function scope and will be deallocated once this transaction/message is processed. So what will remain in structs[msg.sender] after addStruct has been called? Will changing updateStructs(S sInstance) to updateStructs(S storage sInstance) change something?

  • 1
    s is not deallocated until addStruct returns. It might be helpful to understand stack frames, or activation records – libertylocked Nov 13 '17 at 4:04
1

When you store a memory variable into storage, then it will just copy the object into storage.

Using the storage keyword in updateStructs actually won't do anything. Structs and arrays in functions are by default storage variables, so the memory object would actually be copied to storage right when you call updateStructs without the storage keyword.

Edit: I stand corrected. @LibertyLocked is correct in their answer where they say you can't implicitly convert from memory to storage, so adding the storage keyword to the argument will make compilation fail.

  • Thanks for noticing, edited it. Sure about this, tried it? That is most obvious to me, but I wanted confirmation – KwahuNashoba Nov 13 '17 at 15:18
  • My first part, yes. It'll copy the object in memory into storage. You can test this in Remix by adding a getStruct function that fetches the data of structs[msg.sender] from another transaction. If it wasn't copied to storage, then this data wouldn't exist. I was incorrect about my second part, as @LibertyLocked was correct in saying that there would be a compilation error. I updated my answer as well. – flygoing Nov 13 '17 at 16:35
2

In your example, if you'd change your code so that it looks like this

function updateStructs(S storage sInstance) private {
    structs[msg.sender] = sInstance;
}

function addStruct (uint _a, uint _b) payable {
    S memory s = S({a: _a, b: _b});
    updateStructs(s);
}

The code will not compile for the following reasons:

  • updateStructs expects a struct instance from storage, but you are passing a struct instance in memory to it.
  • There is no way for Solidity to figure out where to allocate and place the s in addStruct in storage.
  • When you have S memory sInstance in your updateStruct, your s is copied into the stack frame of updateStructs. Also the s in addStruct is not deallocated until you return from addStruct (otherwise how would the function continue to run once it returns from updateStruct?).

To answer your question, in your example you must use memory instead of storage. In fact, it is implicitly memory in function arguments.

  • OK, to sum it up: at S memory s = S({a: _a, b: _b}); the memory variable will be created in scope of addStruct, then, on updateStructs(s); the value of s is copied into the stack frame of updateStructs and then the s is copied from stack frame into the storage on structs[msg.sender] = sInstance;, right? My question was what will be at this location in storage. – KwahuNashoba Nov 13 '17 at 14:35
  • There is only one storage location and that is structs[msg.sender]. It is only modified in updateStructs. storage keyword means a reference to storage location – libertylocked Nov 13 '17 at 14:37
  • So if you assign reference type variable that is in function stack to variable that is in storage, that variable is copied in storage? – KwahuNashoba Nov 13 '17 at 14:39
  • The reference can get copied but the storage itself is never modified until you write to the reference – libertylocked Nov 13 '17 at 14:41
  • I think we find it hard to understand each other:)This is how I understand it: If you pass s to updateStructs it allocates bytes on stack and s keeps reference to those bytes. Once you do structs[msg.sender] = sInstance what happens? If the reference s is copied it does not make any sense since the stack allocated bytes are valid only in function scope and structs[msg.sender] will be pointing to randome bytes depending on the node it runs on. If the stack allocated bytes are copied to structs[msg.sender] it's fine but I still did not get confirmation on this from your answers. – KwahuNashoba Nov 13 '17 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.