Data Location Assignment

Nice solution @Andre_Mark :ok_hand:

The local variable user is assigned the user’s record (an instance created from the User struct) from the mapping. In the mapping the user’s record is saved in persistent storage, but the copy previously saved in the local variable was stored temporarily in memory. The fact that, previously, the mapping wasn’t updated with the new balance, wasn’t to do with “connection”, but because the new balance was assigned to the temporary local variable and then lost when the function finished executing. It was never assigned to the persistent user’s record in the mapping.

By assigning the user’s record to a local storage variable, we don’t create a local copy of the user’s record. Instead, we create a local “pointer” which directly references the user’s record in the mapping — a temporary “bridge” to the mapping during execution of the function. Any value assigned to this local “pointer” variable, before the “bridge” disappears when the function finishes executing, is effectively updating the user’s record stored persistently in the mapping.

Also, for your next assignments, please don’t post screen shots, because we can’t copy and paste your code in order to execute and test it, if we need to. Instead, follow the instructions in this FAQ: How to post code in the forum.

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

1 Like

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 @Ben17 :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 makes more sense to do what you have done, and add an amount to the existing balance using the addition assignment operator += (instead of the assignment operator = ). However, if you add to, instead of replacing, the existing balance, I think your code would be clearer and more readable if you also change the name of the balance parameter to amount , because you are adding an amount to the balance, rather than replacing it with a new balance.

Let me know if you have any questions :slight_smile:

Thank you for your comment and input jon_m.

That’s a really good point, I didn’t notice that at first but that would definitely make it clearer and more readable. I’ll try to keep alert for situations like that in the future.

1 Like

Thanks for the feedback and I am having a good experience so far!!

1 Like

this my solutions,
at function:
function updateBalance(uint id, uint balance) public {
User memory user = users[id];
user.balance = balance;
}
i changed for :function updateBalance(uint id, uint balance) public {
users[id].balance = balance;

}

checked at remix works.

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 {
         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 {
         User storage user = users[id];
         user.balance = balance;
    }

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

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

Changed memory to storage in line 16.

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

Came up with two different solutions!

Solution #1 Removed User memory user = user[id] and just updated it directly
function updateBalance(uint id, uint balance) public {
users[id].balance = balance;
}

Soltuion #2 change the dataLocation type from memory to storage
function updateBalance(uint id, uint balance) public {
User storage user = users[id];
user.balance = balance;
}

2 Likes

I updated the new user variable introduced in the updateBalance function so it is stored in storage rather than memory. This means the new balance will persist once the updateBalance function has finished executing:

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

1 Like

Hey, the problem was that User was stored in memory so it was only stored temporarily.
I changed that to storage so that it will be stored permanently:

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

1 Like

I hope this is correct it seemed like the most logical and the contract worked. Is there a better way to do this or am i just not grasping?

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

ok i just watched the video thank you!

1 Like

Nice solution @WMXgTW6wh :ok_hand:
… and welcome to the forum! It’s good to see you here, and I hope you’re enjoying the course :slightly_smiling_face:

Hi @Chazzman22,

So, I’m sure you’ve already seen the alternative solution in the video…

users[id].balance = balance;

It’s not that one solution is better than the other — they are just alternatives. It was probably easier to come up with your solution, because it only involved changing the data location of the local variable from memory to storage. However, the actual code with the local storage variable is actually quite complex to understand. It works because, instead of a local copy of the user’s record, we create a local “pointer” which directly references the user’s record in the mapping — a temporary “bridge” to the mapping during execution of the function. Any value assigned to this local “pointer” variable, before the “bridge” disappears when the function finishes executing, is effectively updating the user’s record stored persistently in the mapping.

The solution I prefer, because it’s more concise, and easier to understand conceptually, I think, is the one-liner I’ve included above. This doesn’t involve any local variables, or having to state the data location. All we do is assign the new balance to the balance property of the user’s record (instance) in the mapping (the record of the user with the ID input into the mapping together with the new balance). We are still using the concept of data location to ensure the new balance is saved persistently and not lost when the function finishes executing, because the mapping is a state variable and saved in storage. All state variables are saved in storage, so we don’t have to explicity state their data location.

Just lt me know if you have any questions :slight_smile:

1 Like

I used the mapping storage location directly instead of creating additional storage references. This turned into a single line of code in the function to execute. Although using memory is lower in gas cost it’s the reason for the issue at hand.

2021-07-16_22-36-48

Actual Code:

// SPDX-License-Identifier: MIT
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

Nice solution @jCA :ok_hand:

In actual fact, defining a local variable in memory consumes more gas because it creates a separate copy of the User instance. Your solution, and also the alternative which turns the local variable into a “pointer” by changing memory to storage, are both cheaper in terms of the gas cost, and both consume more or less the same amount of gas, because they effectively perform the same low level operations.

Also, for your next assignments, please don’t post screen shots, because we can’t copy and paste your code in order to execute and test it, if we need to. Instead, follow the instructions in this FAQ: How to post code in the forum .

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

1 Like

Thank you for clarifying that point about memory. I would like to learn more about how to correctly apply memory and storage in terms of gas efficiency.
Thanks for your help!

Thanks for pointing out the screenshot vs code only.

Joe

1 Like