Data Location Assignment

Thanks for the detailed explanation.

1 Like

sure it was just a mistake, thanks though :slight_smile:

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

OR

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

I think that with the second solution I will use more gas because there will be another struct stored into the blockchain. :thinking: But this is a simple smart contract. Probably for a complex one, creating a new struct is necessary.

1 Like

Nice alternative solutions @ballack13r :ok_hand:

In actual fact, even though the code is quite different, the low level operations performed by your second solution (using a local storage variable as a “pointer”) are effectively the same as those performed by your first solution …

If I explain how the second solution works, then, hopefully, you’ll see why …

The important thing to understand is that this first line of code doesn’t create or store a copy of the User instance. Instead, by assigning users[id] to a local storage variable, we create a local “pointer” called user. This only points to (references) the users[id] already saved in persistent storage in the mapping (the struct instance which is mapped to the id input into the updateBalance function). It’s like a temporary “bridge” to the mapping during execution of the function.

In this 2nd line of code, 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” is lost when the function finishes executing.

As I’ve explained above, the local storage pointer doesn’t create a separate copy of the struct instance. However, you are thinking along the right lines by suggesting that the second solution could be useful in a more complex contract…

From within a function, it is certainly simpler and more concise to just assign data to persistent storage by referencing a mapping directly, as you do in your first solution …

But, sometimes, using a local storage variable as a pointer can be a useful intermediate step when performing more complex operations involving multiple properties and their values: it can help us to set our code out into separate logical steps, making it easier to read and understand.

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

1 Like

It is very clear now. Thanks a lot for the explanations! :smiley:

1 Like

Hi Eric,

I’ve answered your additional questions in different discussion topics, which are more suited to their subject matter. You should have received notifications from those particular discussion topics, but I’ve also included links to my replies below.

https://studygroup.moralis.io/t/ethereum-advanced/8420/407?u=jon_m

https://studygroup.moralis.io/t/bitcoin-basics-discussion/8417/683?u=jon_m

Hi Jonathan

Sorry to respond so late because I had to travel out-station to attend a wedding for the weekend. I went through your answers and definitely much clearer now. I really appreciate your meticulous and systematic method of highlighting the answers to my questions. It really brings out a better understanding on a subject which I find sometimes even difficult to put into a meaningful question statement to ask! Thanks.

Eric

1 Like

I simply removed the unnecessary User struct array and updated the state mapping directly in the function.

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

Yes Jonathan,

I received all the answers under different discussion topics. I really appreciate you responding to all my questions even during weekends.

Thanks
Eric

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

Hi Eric,

Really glad you’re finding the answers and explanations so helpful :slight_smile:

I know it can often be quite a challenge to put questions about these topics into words, but you’re doing a good job at expressing yourself. And I always try to consider and reply with what I think the student needs to understand.

I hope you had an enjoyable weekend at the wedding!

Jon

1 Like

Nice solution @jeffalomaniac :ok_hand:
… and good to see you back here in the forum :slight_smile:

Just a quick comment about this part of your explanation …

User memory user = users[id];

This line, which you removed, doesn’t create a copy of an array. You are right that users[id] is created from the User struct; but the correct term is struct instance. It’s based on the “template” provided by the struct, with values assigned to the pre-defined properties. If you want to refer to an instance based on a specific struct, you can refer to the name of the struct e.g. User instance

Let me know if you have any questions.

1 Like

The struct instance makes sense now, thank you for clearing that up. Good to be back. These courses are way more helpful than the udemy courses I was trying.

1 Like

Good to hear @jeffalomaniac :grinning:

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

}`

I just tweaked the code by changing memory to storage but I honestly got lost on the mapping.

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

}

Hum, now I know the solution ; in my mind “updateBalance” could be compared as an update. That’s why I put this “+=” in updateBalance function.

But, as the given solution is “= balance”, in my mind I think more about a “resetBalance” name function, instead of “updateBalance” name function.
But, it’s just a different point of view and it was not the most important here :stuck_out_tongue:

Thanks for these perfect courses and this forum.

1 Like

Nice solution @Talk2coded,

You didn’t need to change the mapping. Do you mean that you don’t understand how the mapping works? …or how the local storage variable is able to update the mapping directly?

If you explain a bit more about what it is you haven’t understood, then we can help you :slight_smile:

You’ll also find a lot of useful explanations about the more challenging aspects of this assignment in other posts in this discussion topic. It’s worth having a browse.

Nice solution @_Vincent :ok_hand:

… and great analysis, explaining why you chose to use += instead of =
This is exactly the kind of reflection we want to encourage :muscle:

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 does make more sense to do what you’ve done, and add an amount to the user’s 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 looks clearer and more readable if we also change the name of the balance parameter to amount, because we are adding an amount to the balance, rather than replacing it with a new balance.

There is always more than one solution to these assignments, and alternative interpretations are equally valid.

Glad to hear you’re enjoying the courses! Just let us know if you have any questions.

1 Like

the mapping syntax confuses me a lot, especially when I’m not finding the balance of an address.
also arrays are a little bit confusing because it seems like you can declare it with anything without using any keyword.

Problem with function updateBalance() : It was storing the new balance in memory. Problem with memory is that value is only stored while function is being called and when function is completed the value is gone. So memory operates as a temporary storage and is not permanent after function execution. Storage operates differently and it stores the value even between function calls, so the storage of value is persistent.

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