When we declare dynamic data types we need to specify the location to store them. Dynamic data types in Solidity are arrays
, strings
, struct
etc. One of three locations of storage
, memory
and calldata
are usually specified.
Using storage
as a location means the data is stored on the blockchain while memory means the data is saved in memory
and will be erased after the function in which it was declared has finished executing. The other location of calldata
is used to stored variable that is passed as an input parameter to a function.
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.7.0 <0.9.0;
contract StorageLocation {
Person[] public persons;
struct Person {
string name;
uint age;
address personAddress;
}
constructor() {
Person memory newperson = Person({
name: "Jamie",
age: 33,
personAddress: msg.sender
});
Person memory personTwo = Person({
name: "Bones Man",
age: 33,
personAddress: msg.sender
});
persons.push(newperson);
persons.push(personTwo);
}
function loadPerson() public view returns ( Person[] memory ){
return persons;
}
function changeDataone() public view {
Person memory person = persons[0];
person.age = 56;
}
function changeDataTwo() public {
Person storage person = persons[0];
person.age = 76;
}
function receiveAsCallData(uint256[] calldata a) public {
//you can not modify a
}
}
The smart contract above is a simple smart contract. At the top of the smart contract we defined an array of Person
which is a user defined struct. The array of Person
is stored in a variable called persons
. The persons
variable is stored on the blockchain because it is a state variable so therefore, it's location is storage
.
Looking at the constructor function and you will see that there are two Person
type created and pushed into the Person[]
array called persons
. The newperson
variable inside the constructor is saved in memory and will be destroyed after the constructor function has finished running. After creating the newperson struct it was pushed into the persons
array for permanent storage. The temporary newperson
variable in memory will be discarded after the constructor function finish executing.
Take a close look at both the changeDataone
function and the changeDataTwo
.You will notice that changeDataone
function creates a variable named person and saved it to memory.
Person memory person = persons[0];
Doing it this way reads the data of the persons
array at index of 0
and stores it in a person variable in memory. Attempting to change person.age
to 56 will not be successful because we are not referencing the storage location of persons
. At the termination of the function the persons
storage variable remains the same because the data in memory has be wiped off.
To persist the changes we make in a dynamic data type we must reference the storage location of that variable. The function changeDataTwo
does that. Try it out on Remix
When the contract is deployed, two Person
struct is added to the persons
array via the constructor which you can view by running loadPerson
. Run the function changeDataone
and observe if the first Person
struct changed as explained. Then run the function changeDataTwo
and you will notice that the persons
variable was updated.
The third location calldata
is used to pass input parameters to functions. Storing a variable in calldata
means that the variable cannot be modified. Using calldata
has the potential of saving some gas as the EVM
does not copy over the data stored on it as it does when you used memory
as a location.
Summary
You make use of memory
location when you want to read a dynamic data stored in state without modification or you need a temporary store for a dynamic data type.
You make use of storage
when you want a reference to the dynamic data stored in state for the purpose of modification.
If you won't be changing the value of the dynamic data type passed as input to a function, you can make use of calldata
. Usingcalldata
helps to save gas because the EVM
does not do a copy of the data.
Top comments (0)