Hi @ekr990011,
Both solutions are correct and equally valid 
What I would say, though, is that the one-line solution …
users[id].balance = balance;
… is shorter, more concise and, in this particular example, also clearer. However, 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.
Additionally, even though setting up the storage pointer with a local storage variable results in more code initially, the actual reference to the pointer ( user
) within the function body is shorter than referencing the “pointed-to-value” ( users[id]
) directly. So, if we needed to use the pointer multiple times within the same function body, then in this case we could ultimately end up with shorter code using the storage pointer and, probably more importantly, more readable code as well.
From tests that I’ve performed previously, it’s also interesting to note that both of the alternative solutions we are discussing consume more or less the same amount of gas. Using a storage pointer, and keeping 2 lines of code …
User storage user = users[id];
user.balance = balance;
… is only very very slightly more expensive than the more concise one line solution …
users[id].balance = balance;
With optimization turned on, the gas cost is exactly the same. Gas consumption, and therefore gas cost, is based on which low level operations (op codes) are performed by the EVM. My understanding is that the gas costs are the same for both of these alternative solutions because, even though the code is different, once compiled into bytecode, both are essentially performing exactly the same low level operations.
Let me know if anything is unclear, or if you have any questions 