Data Location Assignment

pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         User storage user = users[id];
         user.balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}

I just changed memory to storage. Interesting looking above that others just simply removed any reference of memory or storage. I did however work through the problem like in the video before watching it. Kinda saw where we were going and so just gave it a shot.

1 Like
pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         User storage user = users[id];
         user.balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}
1 Like
pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         users[id].balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}

1 Like
pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         users[id].balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }
}
1 Like

Thanks to all the commenters of the post for solving this problem. I often have problems with this, and I even opened this forum just because I was looking to solve it.

1 Like

Hi @ekr990011,

Both solutions are correct and equally valid :ok_hand:

What I would say, though, is that the one-line solution …

users[id].balance = balance;

… is shorter, more concise and, in this particular example, also clearer. However, sometimes using a local storage variable as a pointer can be a useful intermediate step when performing more complex operations involving multiple properties and their values: it can help us to set our code out into separate logical steps, making it easier to read and understand.

Additionally, even though setting up the storage pointer with a local storage variable results in more code initially, the actual reference to the pointer ( user ) within the function body is shorter than referencing the “pointed-to-value” ( users[id] ) directly. So, if we needed to use the pointer multiple times within the same function body, then in this case we could ultimately end up with shorter code using the storage pointer and, probably more importantly, more readable code as well.

From tests that I’ve performed previously, it’s also interesting to note that both of the alternative solutions we are discussing consume more or less the same amount of gas. Using a storage pointer, and keeping 2 lines of code …

User storage user = users[id];
user.balance = balance;

… is only very very slightly more expensive than the more concise one line solution …

users[id].balance = balance;

With optimization turned on, the gas cost is exactly the same. Gas consumption, and therefore gas cost, is based on which low level operations (op codes) are performed by the EVM. My understanding is that the gas costs are the same for both of these alternative solutions because, even though the code is different, once compiled into bytecode, both are essentially performing exactly the same low level operations.

Let me know if anything is unclear, or if you have any questions :slight_smile:

2 Likes

Nice solution @0xtravis :ok_hand:

… and welcome to the forum! I hope you’re enjoying the course :slight_smile:

1 Like

Hi @zacheryurueta,

Welcome to the forum! … I hope you’re enjoying the course :slight_smile:

Yes … you’ll find lots of helpful information and support in these discussion topics. If you find yourself struggling with an assignment, but still want to try to solve it before seeing any solution code, you can always post a question here, and you may find the answer helps you to make more progress on your own :muscle:

pragma solidity 0.8.7;

contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint amount) public {
        users[id].balance = amount;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}
1 Like

just changed “memory” to “storage” in the updateBalance fx and it worked for me.

pragma solidity 0.8.7;

contract MemoryAndStorage {

mapping(uint => User) users;

struct User{

    uint id;

    uint balance;

}

function addUser(uint id, uint balance) public {

    users[id] = User(id, balance);

}

function updateBalance(uint id, uint balance) public  {

     User storage user = users[id];

     user.balance = balance;

              

     //User memory user = users[id];

     //user.balance = balance;

}

function getBalance(uint id) view public returns (uint) {

    return users[id].balance;

}

}

1 Like

It’s good that there will always be people like you who will write a solution to the problem and describe their actions. I appreciate it and am very glad to have responsive people on the internet. I’d have needed it two weeks ago when I lost all the data from my phone and had to contact https://www.salvagedata.com/memory-card-recovery/ to get everything back. Of course, these specialists returned all the files to me, but now I also know the purpose of the data location, thanks to you.

1 Like

Nice solution @mjbam93 :ok_hand:

… and good to see you back here in the forum! :slightly_smiling_face:

pragma solidity ^0.8.0;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
        users[id].balance+=balance;
       
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}
1 Like

I took out the memory to at first see what error code i would recieve.
then i replaced memory with Storage and it worked just fine.

`pragma solidity 0.7.5;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

function addUser(uint id, uint balance) public {
    users[id] = User(id, balance);
}

function updateBalance(uint id, uint balance) public {
     User storage user = users[id];
     user.balance = balance;
}

function getBalance(uint id) view public returns (uint) {
    return users[id].balance;
}

}`

1 Like

Nice solution @Xabier22 :ok_hand:

Even though the initial idea of this assignment is to update the existing balance by replacing it with the new one, I actually think that it does make more sense to do what you’ve done, and add an amount to the user’s existing balance using the addition assignment operator+= (instead of the assignment operator= ).

However, if we add to, instead of replacing, the existing balance, I think the code looks clearer and more readable if we also change the name of the balance parameter to amount, because we are adding an amount to the balance (to give a new total balance), rather than replacing it with a new balance.

There is always more than one solution to these assignments, and alternative interpretations are equally valid.

Let me know if you have any questions :slight_smile:

pragma solidity 0.7.5;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

function addUser(uint id, uint balance) public {
    users[id] = User(id, balance);
}

function updateBalance(uint id, uint balance)  public {
     User storage user = users[id];
     user.balance = balance;
     

}

function getBalance(uint id) view public returns (uint) {
    return users[id].balance;
}

}

1 Like

Problematic part :

function updateBalance(uint id, uint balance) public {
         User memory user = users[id];
         user.balance = balance;
    }

With the above format, the variable user is stored in the memory, which means that after the function is done running, the variable will be erased.
When we can getBalance function, we check the balance of the mapping users.
But in the update function, we never update users…we only update a temporary variable that is deleted after the function is done running.

Some possible solutions :

  1. Here we directly update the balance for the specific (id) user
function updateBalance(uint id, uint balance) public {
         users[id].balance = balance
    }
  1. This is how to fix the current code without deleting any part of it (but it’s not a smart solution as the variable user is pointless)
function updateBalance(uint id, uint balance) public {
         User memory user = users[id];
         user.balance = balance;
         users[id].balance = user.balance;
    }
  1. (After watching solution video)
function updateBalance(uint id, uint balance) public {
         User storage user = users[id];
         user.balance = balance;
    }

What I don’t understand with solution 3 though is :
How can this work ?
So yes, we successfully create a permanent variable (user) that stores the current data in users[id].
Then we update the balance of user (user.balance = balance). This is all fine, but where do we say that users[id].balance = user.balance ?

Because in the getBalance function we get the balance with users[id].balance, but after solution 3 is applied I don’t see where the new balance is stored in users[id].balance

We say user = users[id], but not users[id] = user. Can someone explain me ?

1 Like

Hey Seb,

Great analysis, alternative solutions, and questions :muscle:

Good … here, we directly assign the new balance to the balance property of a specific User struct instance which is already stored persistently in the mapping.
Don’t forget the semi colon at the end of each statement in the function body :wink:

Correct … and you’ve drawn the right conclusion. Although the code is correct and works, it is long-winded and wastes gas. This solution consumes the most gas because the local memory variable creates a temporary copy of the User instance (stored in the mapping) which is assigned to it. Solutions 1 and 3 do not create copies of the data (I explained below why the local storage variable doesn’t)

No, we don’t …

When we define the data location of a local variable as storage , this creates a storage pointer. This only points to (references) the value already saved in persistent storage which is assigned to it (in our case users[id] ); it doesn’t create or store a separate copy of this value, as it would if we defined the data location as memory . A storage pointer is like a temporary “bridge” during execution of the function to wherever in the contract state it points to (in our case a specific User struct instance in the mapping).

Any value which is then assigned to a property of the local “pointer” (user) will effectively update that same property of the specific User instance in the mapping which is referenced by the pointer. This enables a specific user’s balance (stored persistently in the mapping) to be reassigned with the new balance (input into the function), before our “bridge” disappears when the function finishes executing…

user.balance = balance;

I think you will also find this post an interesting read. It goes into more detail by comparing solutions 1 and 3 in terms of their specific advantages and the gas cost.

Just let me know if you have any further questions :slight_smile:

Awesome thanks, I’ve just realized that I didn’t get the notion of storage local variable. Makes sense thanks.

1 Like
pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         users[id].balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}
2 Likes