Data Location Assignment [OLD]

Changing data location from “memory” to “storage”, therefore the data updated is stored permanently and still remain after the function call.

So change this line,
User memory user = users[id];
with this line,
User storage user = users[id];

1 Like

Hi @Nityam_Jigyasu,

Many apologies! I started writing a response to your additional question, but I’ve only just realised I hadn’t finished writing it :pray:

Correct :+1:

Here, we are defining our mapping with the name users. And then, in the same way that we declare the type of data we are storing in our state variables (e.g. uint id; or  address owner;) here we do the same and declare the type of data our keys and their associated values will be, in preparation for actual assignment later.

Maybe what is confusing is the fact that, as a mapping is a data structure, we actually need to declare three data “types” (actually 1 data structure which contains 2 data types), whereas with a state variable we only need to declare 1 data type  i.e.

// Mapping                                            // State variable

   mapping(uint => User) users;                          uint height;
//    3     2       1      N                               1    N

/*                             1  data type of the value
                               2  data type of the key
                               3  data structure type
                               N  name of the mapping or variable              */

I hope that makes it clearer. But do let us know if there is still something that doesn’t make sense.

1 Like

Hi @Lunasea,

Your code works, but all you are doing in the additional line of code is repeating the functionality of the addUser function. This makes the previous two lines superflous i.e. they can be removed as they don’t affect the third line that you’ve added.

However, instead of updating both the ID and the balance, we only want to use the updateBalance function to update balances of existing users. The ID will already have been assigned in the addUser function and, by its very nature, won’t need to be updated like the balance. We only need to include the ID as an input parameter in order to be able to map the new balance to the correct user.

So we only want to update the mapping’s permanent storage with a user’s new balance, which can be achieved by either:

  1. Keeping the original first two lines of code and adding a line that updates the mapping with a user’s new balance, after it has just been assigned to a User instance stored temporarily in memory.  i.e.
function updateBalance(uint id, uint balance) public {
   User memory user = users[id];
   user.balance = balance;
   users[id] = user;
}

or

  1. Replacing the original two lines of code with:
function updateBalance(uint id, uint balance) public {
   users[id].balance = balance;
}

I hope that’s clear. Just let us know if you have any questions.

Keep on learning! :muscle:

1 Like

Hi @Li_Sun,

Your solution works, but you can remove the following line of code from your updateBalance function:

user.id = id;

Assuming we are only going to use this function to update balances of existing users, the user ID will already have been assigned in the addUser function. And so the above additional line of code is unnecessary. We only need to include the ID as an input parameter in order to be able to map the new balance to the correct user.

I hope that is clear. Let us know if you have any questions.

Keep on learning! :muscle:

1 Like

Thank you very much, It helps in understanding mapping data structure. Actually, spending more time behind understanding internal working and behavior helps before we deep dive and move ahead working on professional projects. Thanks again.

1 Like

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

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

The balance was being held in memory so was never being updated to the storage when you change User memory user = user[id] to User storage user = user[id] the balance will get updated in the memory.

1 Like

Hi There

Simple method to fix code…

pragma solidity 0.5.1;

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];
     user.balance = balance;
     
     /// fixed missing update
     users[id] = User(user.id,user.balance);
     ///
     
     
}


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

}

pragma solidity 0.5.1;
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’ve tried running the code I’ve seen in a lot of answers. I understand the tweak, that memory needs to be changed into storage because it changes the state of the function from temporary to permanent.
However when I run 1,10 -> addUser ->getBalance -> I get string 0
and when I addbalance 1,20 ->getbalance -> I still get string 0
Can anyone explain to me why I’m not getting the desired answer? I’ve tried putting it in “quotationmarks” and it still gives me the same result

pragma solidity 0.5.1;
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

@filip
Hi. I had to change pragma solidity to 0.5.12 otherwise with google-chrome it won’t compile.

To differentiate addUser and updateBalance I added some constraint.

pragma solidity 0.5.12;

contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}


modifier newUser(uint id) {
    require( id > 0, "User id must be > 0" );
    require( id != users[id].id, "User id is already used" );
    _;
}

modifier userMustExist(uint id) {
    require( id > 0, "User id must be > 0" );
    require( id == users[id].id, "User doesn't exist" );
    _;
}

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

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

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

}

1 Like

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

1 Like

Hi @JeffE,

I think you meant to say: “… the balance will get updated permanently in storage”. :wink:

1 Like

Hey @jon_m,

Yes you are right! Thank you for pointing that out!

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

The function is assigning a copy of the User to memory from the mapping and updating the balance of the copy. The copy is then destroyed after execution of the function. To persist the balance, one way is to directly update the balance of the User referenced by the mapping:

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

Since users was already in storage just need to replace the current balance of the index of id in users to the new balance. There shouldn’t be a need to use memory or any extra variables, right?

pragma solidity 0.5.1;
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

Hi, I change memory to storage in the following function to have the modification be stored after the function runs:

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

@mikekai Yes, you are right

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

Just need to change the users[id] balance directly. Commented out the other section.

2 Likes

pragma solidity 0.5.12;

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

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

}

1 Like