Data Location Assignment

Nice solution @MichaelAudire :ok_hand:

… and on the whole, your explanation is good. Here are some additional comments …

Yes … it was a temporary copy of the whole User struct instance (stored in the mapping) which was initially stored in memory …

User memory user = users[id];

Then, the new balance was assigned to the balance property of this temporary copy, where it was only saved until the function had finished executing, at which point it was lost …

user.balance = balance;

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 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;

And because …

… the user’s new balance will also be stored persistently in the mapping, until it is updated again.

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

1 Like

Here i added a require statement to check that the balance input is not 0, then i set the value of the balance in the User struct to the input balance.

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 {

         require(balance != 0, "Add a vild amount");

         //User storage user = users[id];

         //user.balance = balance;

         users[id].balance = balance;

    }

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

        return users[id].balance;

    }

}
2 Likes

Nice alternative solutions, @Arch.xyz :ok_hand:

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

Your require() statement is correctly coded, but why can’t a user’s balance be updated to zero?

Yes i am, and im really looking forward to starting the second course :sunglasses:

1 Like

Hi everyone, very happy to be joining the forum. I think the easiest way would be to have the user in the updateBalance function, be stored in storage.

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.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;

    }

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

        return users[id].balance;

    }

}
1 Like

Nice solution @Giupi :ok_hand:

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

Just to clarify what’s happening with this particular solution …

When we define the data location of a local variable as storage , this creates a storage pointer, in our case user . This only points to (references) the value already saved in persistent storage which is assigned to it, in our case users[id] (a specific user record). The storage pointer (user) itself isn’t saved to persistent storage.

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 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;

So, it’s the new balance which is saved to storage, not user

Let me know if anything is unclear, or 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 memory user = users[id]; //Here is the error, it is creating a temp var with the value of the state var, we have to change the state var to get the desired result
         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

My solution was to in the updateBalance function, change the data location of user from memory to storage

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 @Seva_Rybakov :ok_hand:

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

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;
        // users[id] = User(id, _balance);
    }

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

}
1 Like

Nice solution @CryptoUsic :ok_hand:

Your alternative “commented-out” solution also works, but you are right not to choose it as your main solution, otherwise the updateBalance() function would perform exactly the same operations as the addUser() function …

This isn’t efficient, and we might as well remove one of the functions and just call the remaining one something more general, such as update(). But that would also hardly be ideal. What we want is an updateBalance() function with code that only updates the balance property for a particular user ID (which is what your main solution does) and not the whole User instance. We only need to create a whole new User instance when we add a new user, because once added, their user ID won’t need to change (at least not at the same time as their balance).

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

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;   //Here was the error

    }

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

        return users[id].balance;

    }

}

The error was in that the previous code creates a variable in the memory sotrage, and use this variable to change the balance. Yes in fact when the function is executing the balance changes but when the functions ends remove the change because was in the memory storage.

1 Like

You just need to change from memory to storage, I think this is the right way, if not please, someone correct me

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]; // basically just changed memory to storage making it permanent

     user.balance = balance;

}

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

    return users[id].balance;

}

}

1 Like

I simply set the balance directly in the users map. I don’t think we need to create a new instance inside the function.

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 @IvanFitro :ok_hand:

… and a good attempt at explaining what the problem was with the original code :muscle:

Just to clarify … in the original code, the balance is only ever changed in the balance property of the local variable user, which is lost as soon as the function finishes executing. This local memory variable holds a temporary copy of the user’s record stored persistently in the mapping; and the user’s record in persistent storage is never modified. But your solution solves this by assigning the new balance directly to the user record in the mapping.

Welcome to the forum @QuarterChap! … I hope you’re enjoying the course :slightly_smiling_face:

Yes … your solution is correct :ok_hand: It’s not the only way … there are several possible alternatives, one of which you will have seen in the solution video.

Hi @tbem,

Good to see you back in the forum :slightly_smiling_face:

Yes, you’re right … we can solve this effectively and concisely without having to use a local User instance variable :ok_hand:

My solution here :slight_smile:

pragma solidity ^0.8.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