Data Location Assignment [OLD]

i couldn’t think it straight to find the solution so i watched the solution and as you did it my brain remind me of class and object from my c++ knowledge so i just call my object which is the balance into the class which is users[id]. below is my code :

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

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

}

1 Like

Hi @Phaxsam,

Your solution code is correct :ok_hand: but what the code is actually doing is…

…assigning your new balance value (2nd input parameter) 

    TO the balance property of the specific User instance WHICH IS

         mapped to the key with a value of id (1st input parameter)
         AND
         stored persistently in the users mapping  IN  the contract state


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

Apologies for my ignorance, but I don’t understand what this command is doing…

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

why are we just specifying “id” and not “balance” in the left side of the equal sign?

1 Like

Hi @Yutaro_Shimizu,

Sorry for the delay in answering your question.

In our contract, the user ID is a property within each User instance (created from the struct) and it is also the key each User instance is mapped to in the mapping.

In the addUser() function, we create a new User instance (based on the struct) with…

User(id, balance);

This assigns the value input as the id argument to the id property, and the value input as the balance argument to the balance property.

We then need to assign this new User instance to the mapping, and we do that by mapping it to the key, which in this case is also the value input as the id argument …

users[id]

Once this user has been added to the mapping, their data can be retrieved, or referenced, in the same way that it was initially assigned…

users[id]

i.e. by using the key (id)


It may be easier to understand what’s happening if we do the same, but with a key which isn’t also one of the struct properties e.g.

pragma solidity 0.7.5;

contract Employees {

    struct Person {
        string name;
        bool isManager;
    }

    mapping(uint => Person) people;

    function addPerson(uint id, string memory name, bool isManager) public {
        people[id] = Person(name, isManager);
    }

}

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

Jon,

Thank you so much ! Your example and explanation makes so much sense now. One last question, however. Why are we able to use the same variable for the key and for instance in struct? I was thinking this would cause a collision…

1 Like

Hey Yutaro,

I’m glad my explanation has helped make more sense of what’s happening. These coding structures can be difficult to fully understand at first, especially if you like to get to the bottom of things :wink:

The mapping only defines the data type of the keys, which in our example is uint (unsigned integer).

mapping(uint => User) users;

We have just choosen to use the same uint value, input into the function as a parameter with the name id, as both …
(i) the value assigned to the 1st struct property which also has the name id; and
(ii) the uint key which each User instance we create is mapped to when assigned to the mapping.

The id within the struct is a property name, and the id passed to the function is a parameter name. There is nothing to stop us from using the same name for both. The scope each is defined in is different, and so there is no “clash”.

You are right, though, that we shouldn’t use the same name for a variable and a parameter. If we did, this would generate a compiler warning.


Also, picking up on the other questions you’ve asked about this function in the Additional Solidity Concepts discussion topic …

users[id]   represents the “placement” of our new User instance in the mapping users . We create a new entry in a mapping by assigning it to a new key in that mapping. The keys in the users mapping are defined as having to be unsigned integers, and (as I’ve already mentioned above) for these keys, we are choosing to use the unsigned integers input into our function as the id parameter. The values mapped to each key are defined as having a custom data type User based on our User struct. The User struct has been defined with two properties, and that is the reason why each new value we assign to a new key in the mapping needs to be created with two property values: one for the id property, and the other for the balance property. For these two property values, we use the two values input into our function as the id and balance parameters.

The id in users[id] references the unsigned integer input into our function as the id parameter. It is placed within square brackets and appended to the mapping name users , because (as mentioned above) this is the syntax used to assign a new value to a mapping — achieved by mapping it to a key, which is the value placed within the square brackets. The same syntax is also used to reference an entry which is already stored in the mapping.

I hope this helps to answer some more of your questions. Just let me know if anything is still unclear, or if you have any more questions :slight_smile:

Jon,

Wow, great explanation. Now I fully get it. Thank you so much!

Yutaro

1 Like

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

It makes sense to store the new user data to override the old.

1 Like

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

The function updateBalance must include " users [_id]. balance = _balance " as shown above to make an update to the balance that refers to the specific user. Otherwise we can’t get the “getBalance function” to retrieve the rightful user updates

1 Like

You solution is correct @ecbwong :ok_hand:

The key thing here is that you are now updating the specific user’s balance in persistent storage in the mapping. Originally, we were still updating the specific user’s balance, but only in a temporary, local copy of their record, which was then lost when the function finished executing.

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

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

}

1 Like

This is the part of the contract that has to be changed.

function updateBalance(uint id, uint balance) public {
User storage user = users[id];
user.balance = 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;
         
        // user.balance += balance; in case we would want to keep adding balance instead of updating to the las value
         
        
    }

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

}
1 Like

Nice solution @pieczyslaw :ok_hand:

This is a good observation…

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 suggest here as an alternative, and add an amount to the 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 would be clearer and more readable if we also changed the name of the balance parameter to amount, because we would be adding an amount to the balance, rather than replacing it with a new balance e.g.

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

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

1 Like

The Original Code for this Assignment is Missing the += Operator. Simply switching the Memory to Storage will not Deploy the Contract.

The correct code is user.balance += balance;

hey can you help clear up some of my confusion please?

so the user varible of the User type, that is declared in the updateBalance function is storing an id and a balance… how does the getBalance function call on the user variable if the only code in the getBalance function is return users[id].balance???

I’m having a hard time understanding

Hi can you help clear up some of my confusion please?

so the user varible of the User type, that is declared in the updateBalance function is storing an id and a balance… how does the getBalance function call on the user variable if the only code in the getBalance function is return users[id].balance???

I’m having a hard time understanding

i’m referring to the solution that simply changes memory to storage:

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

Hey @Rob1, hope you are well.

Sorry for the delay on the answer.

If you check the mapping, it will point an uint to a struct based on User properties (id, balance);

So the proper way to access the property of the mapping ID, its by create a function that can look into the mapping, get the desired ID and return its value.

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

Carlos Z

1 Like

Understood thanks :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;

}

}

Well this one works for me and updates the balance, But is it correct ?

2 Likes