Solidity Bits— storage vs. memory
Coding in Solidity is not always intuitive, but it’s fun. One of the small challenges I had to wrap my head around was the difference between storage vs. memory.
Imagine a simple example like the one below
pragma solidity ^0.4.22;contract Fruits {
string[] public items; constructor () public {
items.push('apple');
items.push('orange');
}
}
If I was to deploy this contract, items at the 0 and 1 index would be ‘apple’ and ‘orange’ respectively. Easy enough.
What if I try to create a new array inside of my constructor function pointing to the items
variable array? Seems easy enough.
constructor () public {
...
string[] newItems = items;
}
But wait! we get a warning like the one below!
Understand the warning? I didn’t either.
To fully comprehend what is going on lets discuss how Solidity interprets memory vs. storage.
Storage vs. Memory
Solidity views the storage vs. memory relationship in 2 different ways.
- Contract state data
- storage: variables defined at the top level inside of the contract. (ex:
items
) - memory: Structs ( Refer to http://solidity.readthedocs.io/en/v0.4.21/types.html if you do not know what a struct is)
2. Variable value declaration
- In this scenario, variable value declaration can be defined as storage or memory depending on how you want to save that variable (further explained below)
For our example purpose, items
is a storage state inside of the Fruits contract. With the new variable newItems
, we have two options, save the variable in memory or storage. I will review both options below.
NewItems as storage
If newItems was saved as a storage, you have to include the keyword storage before newItems. Example shown below.
string[] storage newItems = items;
By adding the storage value the warning should disappear. But what exactly is this doing? By adding storage
, newItems
now POINTS to the items
array. In other words, any changes you make to newItems
, will directly affect the items
array.
pragma solidity ^0.4.17;contract Fruits {
string[] public items; constructor () public {
items.push('apple');
items.push('orange');
string[] storage newItems = items;
newItems[1] = 'lemon';
}
}// items[1] will now be lemon
// items[0] will remain the same as 'apple'
In conclusion, the storage key forces the newly created variable to point to the state variable (items) and not a copy. Any changes made to the new variable will directly change the structure of the contract state variable.
NewItems as memory
newItems
has an alternative method to persistence. Instead of storage
, there is a memory
option. The memory
option functions as a copy as opposed to a pointer. Thus, using the memory
key and making mutations on the newly created variable WILL NOT affect the original state variable. Example below.
pragma solidity ^0.4.17;contract Fruits {
string[] public items;constructor () public {
items.push('apple');
items.push('orange');
string[] memory newItems = items;
newItems[1] = 'lemon'
}
}// items[1] will remain the same as 'orange'
// items[0] will remain the same as 'apple'
Conclusion
I hope this resolves any question regarding this warning regarding memory vs. storage. I view storage (state) as a hard drive and memory (local variables, temporary) as RAM. If you anticipate on creating functions that make state changes, storage might be the best option. If you need a copy of the data but not necessarily want to manipulate the contract state use memory.
Side Note
Any value passed into a Contract function will be sent via memory (default). A function that takes that parameter will not manipulate the contract state. Example below.
pragma solidity ^0.4.17;contract Fruits {
string[] public items; constructor () public {
items.push('apple');
items.push('orange');
changeFirstElement(items);
}
function changeFirstElement(string[] newItems) pure private {
newItems[0] = 'lemon';
}
}// items[1] will remain the same as 'orange'
// items[0] will remain the same as 'apple'
If you need to enforce state change, you can add the storage
key in the parameter.
function changeFirstElement(string[] storage newItems) private {
newItems[0] = 'lemon';
}
fin