Data Location Assignment

Hi @KennethJP,

Yes there is… In fact you can remove your 2nd and 3rd lines in the updateBalance function, and your 1st line on its own will still produce the same result…

function updateBalance(uint id, uint newBalance) public {
    users[id] = User(id, newBalance);
}

But this makes the updateBalance function exactly the same as the addUser function.
While this does still work…

So, instead of the 1st line of code in your solution, you can start with your 2nd line…

The local variable user creates a temporary “bridge” to the mapping during execution of the function, so that the newBalance can be added to a specific user’s balance stored persistently in the mapping, before this “bridge” is lost when the function finishes executing.

So, you then want to assign newBalance to the balance property of the pointer you’ve just created, as follows:

user.balance = newBalance;

… and this will update the user’s balance in the mapping.

What you’ve done is re-assigned the balance property of the pointer itself to another local variable (which shouldn’t have the same name as your parameter newBalance, anyway).

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

Nice solutions @DavidV :ok_hand:

Apologies for the delay in answering your question…


Good question! … and, yes, this can be difficult to understand, at first.
The specific user’s instance in the mapping (users[id]) doesn’t need to be assigned the values stored in _user , because _user temporarily is that user’s instance in the mapping. Let me explain…

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

Just let me know if anything is still unclear, or if you have any more questions :slight_smile:

Hi @Cero,

Technically, speaking, you are correct :+1:
However …

I assume you’ve already seen the 2 alternative solutions suggested in the follow-up video. Can you see how they are more suitable?

Nice solution @FerfiC :ok_hand:

Yes… we use the id to reference that user’s instance in the mapping, where it is stored persistently. Then, as you say, we can access the balance property of that user’s instance, and update it with the new balance that has been input into the function.

Hi @Denzel,

Technically, speaking you are correct :+1:
However …

I assume you’ve already seen the 2 alternative solutions suggested in the follow-up video. Can you see how they are more suitable?

1 Like

newBalance gets lost after function is done because it was in memory, now it is in storage and will be stored for ever on the blockchain.


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


1 Like

I didn’t create a new variable with a storage variable like I noticed many people did. I was under the assumption we can directly update the specified user, since we have access to the state variable ‘users’ and the ‘id’ as well. I checked and my way did work. If this is incorrect, would love to hear why we do need to create a storage variable!

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 {
        uint oldBalance = users[id].balance;
         users[id].balance += balance;
         
         assert(users[id].balance == oldBalance + balance);
    }

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

}
1 Like

Hey guys! At first, all I did was change “memory” to “storage” in the updateBalance function.

Then realized I could skip the step where we assign the user to a local variable. And this is my result:

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

Thank You for the explanation. I can see about the efficiency of the code. I also saw the solutions in video.

1 Like

Simpler than I imagined haha, thanks for the help :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;
    }

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

}
1 Like

Nice solution @Jacky_Li :ok_hand:

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

Using a local storage variable to create a “pointer” to a specific User instance in the mapping, is just one alternative, and your solution is equally valid.

That’s correct :+1:

Just let me know if you have any further 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] = User(id, 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

Welcome to the forum @jaejang22! It’s good to see you here, and I hope you’re enjoying the course :slight_smile:

Your solution works, but your updateBalance function is now exactly the same as the addUser function.

I assume you’ve already seen the 2 alternative solutions suggested in the follow-up video. Can you see how they are more suitable?

Simple solution: change user var to storage or just access without creating local var.
I decided to overcomplicate with a function just so I can use a storage parameter as an excercise.

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

After a reasonably long time thinking about how stupid I must be to not understand why the extra user variable was needed, I eventually just messed around and removed it, solving the problem of the memory keyword creating a new local User value that would not persist outside the function.

function updateBalance(uint id, uint balance) public {
//remove loval variable 'user', instead accessing the balance via mapping.
         users[id].balance = balance;
    }
1 Like

image

It was to do with mapping right? because before it was memory, which is a temporary location, and the mapping is located in storage, so they don’t connect, so you change it to; User storage user and they map?

1 Like

I just modified the updateBalance function, so the updated balance doesn’t just stay within the function. Changing from memory to storage does the job.

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

}
2 Likes