Data Location Assignment

Hey Seb,

Great analysis, alternative solutions, and questions :muscle:

Good … here, we directly assign the new balance to the balance property of a specific User struct instance which is already stored persistently in the mapping.
Don’t forget the semi colon at the end of each statement in the function body :wink:

Correct … and you’ve drawn the right conclusion. Although the code is correct and works, it is long-winded and wastes gas. This solution consumes the most gas because the local memory variable creates a temporary copy of the User instance (stored in the mapping) which is assigned to it. Solutions 1 and 3 do not create copies of the data (I explained below why the local storage variable doesn’t)

No, we don’t …

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

I think you will also find this post an interesting read. It goes into more detail by comparing solutions 1 and 3 in terms of their specific advantages and the gas cost.

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

Awesome thanks, I’ve just realized that I didn’t get the notion of storage local variable. Makes sense thanks.

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

}
2 Likes

I tried to find the simplest answer on google because this is all so new to me. I replace the “user memory user” inside updatedbalance with “user storage user”. The logic behind this is because storage is used for the instance stored in the database where memory is referring to a temporary place.

I do have a question though. When I had the initial code that was given in the video on remix and deployed it, I received the green check it was working but I did not have the addUser, updatedBalance and getBalance on the left hand side below (like the video) so I couldn’t test out my solution. Do you know why this is?
Thank you!

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

Nice solution @kcoffey :ok_hand:

Sorry for the delay in replying and giving you some feedback!

Have you manged to resolve this issue? I can see you’ve moved ahead very quickly with the course, so I’m guessing this isn’t a problem any more, otherwise you won’t have been able to test any of your contracts! These sorts of issues often arise when we are first getting used to how Remix works, but then quickly resolve themselves when we become more familiar with the interface. Maybe you just hadn’t clicked on the arrow to the left of the deployed contract name and address (at the bottom of the Deploy & Run Transactions panel) in order to display the contract’s function-call buttons… Anyway, just let me know if this is still an issue, and I’ll help you resolve it.

This is actually a pretty good explanation of why your solution works :+1:

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, this value is whichever specific user record (User struct instance in the mapping) is referenced by users[id]

It’s important to understand that a storage pointer doesn’t create or store a separate copy of the value in persistent storage. A storage pointer is like a temporary “bridge” during execution of the function to wherever in the contract state it points to. This is in contrast to a local memory variable, which does create a separate copy of the value assigned to it, and which (as you say) is only stored temporarily until the function finishes executing.

Any value which is assigned to a property of the local storage 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 …

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

Hi jon_m! Thanks for the feedback! No worries on the delay!
I’m going to copy and paste your reply to my computer files to keep and reread and refresh my mind sometimes, if that’s ok?
I have gone quickly with the course as I only have until the 23rd. Then I’m going to take some time and do multiple Smart Contracts on my own and work with css games, JS before I rejoin again. Just to make sure I understand it all and get fluent so I can understand the next courses better.
Right now I’m working on my multiSig wallet, its challenging but I’m fumbling through! See you over there in a bit!

1 Like

Hey @kcoffey,

Glad you found the feedback helpful :slight_smile:

Yeh, that’s fine if you only copy and paste the text and code of my post. What you shouldn’t do is include any of the wider forum website (design, logos etc.). For example, a screen shot of the whole page is not OK.

If it’s just for your own personal use then just go ahead and copy and paste as I’ve explained above. User Content in this forum is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License

I really appreciate you asking :slight_smile:

By the way, you can bookmark a post you want to find again easily and refer back to, by clicking on the 3-dots icon next to Reply at the bottom of the post. You can then easily access your bookmarked posts by clicking on your avatar/profile pic and then the bookmarks icon. You may still have access to your forum account even after your Academy subscription expires. You’ll need to check that with Support. But that would save you having to copy-and-paste and store the info yourself… just an idea :wink:

Great idea :muscle: Taking your time to experiment with the code you’ve learnt in the course material and doing your own research is an invaluable part of the learning experience.

Thank you for the help and in depth answers. I still cant find getNumber and setNumber. Ive tried everything even going through the plugins and reseting remix.
I also went back to the setter functions video with Filip and put his exact code in. Ive also copy and pasted code straight in the remix and nothing. I’ve tried download remix to another computer browser but it was actually emptier and missing functions then on my own computer. If you have any suggestions that would be great. Thanks!

Hi @kcoffey,

What exactly do you mean here? Are you referring to the video early on in the course which uses a setNumber() setter function to assign an integer to a uint state variable, and then a getNumber() getter function to retrieve the integer stored in this state variable (or something similar)? Do you mean that you aren’t getting an orange setNumber button and a blue getNumber button in the Remix panel, and so you can’t call these functions and interact with the HelloWorld contract?

If you post your actual code — the code you are having problems compiling or deploying — then that should make it easier to identify the actual problem and to know how to help :sweat_smile:

By the way, you should just be opening Remix in your browser, not downloading it — it’s not an app. I use Google Chrome and Remix works for me at https://remix.ethereum.org
Make sure your browser version is up to date. Clearing cookies and the browser cache is also a solution that others have mentioned and is known to solve some issues with Remix, but I’ve never had to do it myself. If you try doing that, then make sure you first back up any .sol files saved in your Remix workspaces which you want to keep, otherwise you will probably lose them.

Also, if you’re using the Brave browser, I’ve heard that you need to adjust some of the browser settings because they cause a few issues with Remix and prevent some of its functionality from working.

I’m also tagging @thecil, in case he can suggest anything else you could try. He knows much more than me about the effects of clearing the cache and what sorts of problems that can solve, or perhaps cause! He should be able to confirm, add to, or correct anything I’ve mentioned above :mechanical_arm:

2 Likes

Hi Jon,

Ok thanks, Ill try this all. My month is up tonight so I will try all your suggestions and hopefully have it fixed by the time I restart my studies with Moralis. T
Its all code haha I’ve tried literally every code Filip has done in the videos. Ive even copy and pasted his stuff. Thanks again!!

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;

}

}Preformatted text

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;
        //most simple and direct way to update the mapping of this user id and its balance that is in 
        storage 
    }

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

}
1 Like

// SPDX-License-Identifier: GPL-3.0

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

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

}

2 Likes
type or paste code here

contract strong{
  mapping(uint=>user)users;
  struct user{
    uint id;
    uint balance;
  }
  function addUser(uint id, uint balance)public{
    users[id] = user(id, balance);

  }
  function uddateBalance(uint id, uint balance)public{
    user[id] = user.balance;
  }
  function getBalance(uint _index) public view returns(uint){
    return balance[_index];
  }

}

Nice solution @JoriDev :ok_hand:

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

This is also a good explanation.

Just to clarify …

users is the mapping, which can store multiple User struct instances
users[id] references the User struct instance with a specific id property

So, your solution updates the balance property of the struct instance with this user ID, which is saved in persistent storage in the mapping.

Let me know if you have any questions :slight_smile:

1 Like

Hi @jahh,

It’s good to see you back here in the forum again after a while :slightly_smiling_face:

I see that you’re making an effort to repeat this assignment, but using an alternative method to last time.

The code you’ve posted first throws a red compiler error (in Remix) for the return statement in your getBalance() function…

You can view the error message either (i) by hovering over the red exclamation mark in a box next to the line number where the error is, or (ii) at the bottom of the Solidity Compiler panel. The error message tells you why there is an error, and  ^-----^  points to which part of the line of code the error relates to. So, this error message is telling us that balance is an undeclared identifier.

An identifier is a name, and balance is the name of a property within your user struct. We can’t just access this property directly. Instead, we use dot notation to access it as a property of the struct. However, the properties of the user struct declaration itself, don’t hold any values. The struct is used as a data-structure template for the creation of specifc user struct instances in addUser() …

… which are assigned to, and stored in, the users mapping (not the user struct), with each instance (effectively a specific user record) mapped to a unique id (the key)…

So, the balance we want the getBalance() function to return is the value stored in the balance property of a specific user struct instance stored in the users mapping. As we are accessing a value in a mapping, not an array, _index is probably not the most suitable name for the input parameter. We look up specific values in a mapping by the key they are mapped to, not by index position. So, it makes more sense for the parameter name to reflect its association with the id property, which in this mapping also serves as the key. First we need to access the specific user instance in the mapping, which we do by appending the value of its key to the name of the mapping: users[_index] , or users[_id] if we change the name of the input parameter to something more suitable.

So, users[_id] references the user instance, and we can then drill down to its balance property by appending balance to that using dot notation …

return users[_id].balance;

Having corrected getBalance(), the compiler will now throw another red error for the statement in your updateBalance() function body …

The new balance that we want to assign, and replace the current balance with, is the balance parameter input into the function. So only balance should be on the right-hand side of the assignment operator.

And we want to assign the new balance to the balance property of the user struct instance in the mapping which is mapped to the same id as the one input into the function as the other parameter. You should be able to see that this is accessed with the same expression as the one used in the return statement in the getBalance() function…

users[_id].balance

… but making sure that the key references the id input parameter by using the same name…

users[id].balance = balance;

The only other thing to mention is that the convention is to start the names of structs and contracts with a capital letter. This isn’t mandatory, but it does help to make referencing clearer, and to avoid confusion, when other elements (e.g. mappings, variables etc.) have almost identical names e.g.

struct User {
   uint id;
   uint balance;
}

mapping(uint => User) users;
/*           v           */
mapping(uint => user) users;

users[id] = User(id, balance);
/*           v             */
users[id] = user(id, balance);

Let me know if anything is unclear, or if you have any questions about any of these points :slight_smile:

1 Like

See below my solution, thx :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

Nice solution @Thunarson7999 :ok_hand:

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

The easiest solution is to replace “memory” on line 16 with “storage”, so that the User id is not just updated locally, but in the mapping portion of the code.

1 Like

Hi @pappas_kellyn,

Correct …

… so that the User struct instance, with the same id as the one input into the updateBalance() function, has its balance property updated in persistent storage in the mapping… and not just locally within the function, where it would only be stored temporarily until the function has finished executing…

… but I’m sure that’s what you mean, and what you understand  :muscle: :smile:

1 Like