Hey Seb,
Great analysis, alternative solutions, and questions
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
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