Of course! Solution in the video is was really obvious . It’s probably more instinctive if I was doing it from scratch to start with
users[id].balance = 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 {
User storage user = users[id];
user.balance = balance;
}
function getBalance(uint id) view public returns (uint) {
return users[id].balance;
}
}
Considering that the problem was memory I rewrote the function addUser to try:
In this way after creating a user when trying to get the balance I get 0. This was expected because “user” it’s only saved in memory and it get lost. Changing memory with store it works.
function addUser(uint id, uint balance) public {
User memory user = users[id];
user.id = id;
user.balance = balance;
}
I don’t understand why this other code works with memory then! Why it works?
function addUser(uint id, uint balance) public {
User memory user; // qui potrei anche mettere User memory user = user[id];
user.id = id;
user.balance = balance;
users[id] = user;
}
Yes, I agree with what you say, @m8rix. There are several alternative solutions to this assignment, though, and both of the ones you have posted are valid. However, we can argue that the one liner is the better one, because it (i) uses less gas, and (ii) is more concise.
Hi @enrico,
You are correct, and the test and experimentation you’ve done with the code in the addUser function is helpful. However, the assignment is to rewrite the updateBalance function, not addUser. If you leave the addUser function as it was, and then change memory to storage (as you suggest) in the updateBalance function, then the updateBalance function will also work.
By adding the line users[id] = user;
your amended addUser function (using memory
) now works, because this code assigns the local User instance (user
) — created and stored only temporarily in memory — to the users
mapping which is located in permanent storage
.
user
can be assigned to the mapping with the key (id
), which is one of the function’s parameters.
By assigning users[id]
to a new local User instance (user
) …
User memory user = users[id];
… we are assigning the data (ID and balance) that is already stored permanently in the users
mapping for the user with the ID passed into the addUser function. Can you see that this is pointless? If it’s a new user we are adding, then users[id]
should not reference an existing user (unless of course the same ID has been used twice). That’s why in the addUser function we would only need to use…
User memory user;
… to declare a new User instance, which we then assign data to on the next two lines.
Now that you, hopefully, understand how this code works, can you use it to modify the updateBalance function, so that it successfully updates an existing user’s balance? There is also another more concise solution, which only requires one line of code. Can you work that out? If you can’t work it out on your own, have a look at some of the posts in this forum discussion and you’ll be able to spot it
Let us know if anything is still unclear, or if you have any other questions.
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 storage user = users[id]; //changed, memory to storage
user.balance = balance;
}
function getBalance(uint id) view public returns (uint) {
return users[id].balance;
}
}
function updateBalance(uint id, uint balance) public {
User memory user = users[id];
user.balance = balance;
users[id] = user; // FIX
}
This updates the balance with the new value by changing memory to storage.
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 storage user = users[id];
user.balance = balance;
}
function getBalance(uint id) view public returns (uint) {
return users[id].balance;
}
}
…
User
storage user = users[id];
…
I must confess that I wrote this solution at the first time after remix gave an error at the relevant line and I think It was being suggested about to change the variable statement to storage
. Warning has gone. Then I remembered memory
variable is short life-time like an M button in calculators, but I didn’t understand it totally.
Later seen the solution video and I failed the quiz of data locations with two theoretical questions then I decided to get the variable type classes from beginning. I checked how the “structs” work and why, I saw setting memory user
variable better, I got the point of memory variable’s value weren’t able to go out of the function.
After that it was easy and seems natural to assign user[id].balance = balance;
It was all about variable statements for me to digest better, and the differences than other object oriented programming languages.
Hi @Atlas,
Well done for going back over the material and ending up with an understanding of the concepts that makes sense to you
It often helps to revisit code we have worked on previously and analyse it in a new light, in the context of new concepts we are learning. By taking this “non-linear” approach to learning you should end up with a better overall understanding, and a solid foundation on which to build more advanced knowledge.
By the way, I’m sure it’s just a slip, but
should be:
users[id].balance = balance;
/* Here, we use the key (id) to assign the new balance to the mapping (users),
and not to the new instance of the User struct (user) */
You 're right! it was a slip, definitely i meant the mapping variable there but must be corrected for the other users. thanks, cheers.
this is my answer, I just added one line after those two lines of code
users[id] =user;
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;
users[id]=user;
}
function getBalance(uint id) view public returns (uint) {
return users[id].balance;
}
}
I really like your solution, it is simple and you explain it very well
A solution inspired by Jshanks21. You store the user data through “storage” and not “memory”. That way the information will actually remain whereas in the “memory” storage the information will disappear after the function is called
function updateBalance(uint id, uint balance) public {
User storage user = users[id];
user.balance = balance;
Added one more line , the last one as below:
function updateBalance(uint id, uint balance) public {
User memory user = users[id];
user.balance = balance;
users[id]=user;
user had all the correct info just was not copied to the mapping.
Hello, here’s my solution.
function updateBalance(uint id, uint newBalance) public {
users[id].balance = newBalance;
}
I just removed the User memory user = users[id];
part because the new balance of the user is just in the memory so it is not permanently stored. Instead I went directly to the mapping and assigned a new value to the user balance. Did I do it correctly?
Removed the local variable, which is only a copy of the mapping variable. Instead set the mapping variable = argument value.
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;
}
}
Just exchange the keyword memory
with storage
.
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 storage user = users[id];
user.balance = balance;
}
function getBalance(uint id) view public returns (uint) {
return users[id].balance;
}
}