Data Locations 🗺️ In Solidity

This article talks about how to use Storage and Memory keywords in Solidity.

Each variable declared and used in a contract has a data location. It specifies where the value of that variable should be stored. In this article, we will go through the brief of data location and the rules. If you are new here, I would encourage you to read previous write-ups on Solidity. 
Remember: The amount of gas you will use during a transaction depends on the data location you use in your smart contract. Hence, the best practice is to write an optimized code that uses a minimum amount of gas.
  
Solidity provides four types of data locations.
  • Storage
  • Memory
  • Calldata
  • Stack

Storage

 
The storage location is permanent data, which means that this data can be accessed into all functions within the contract. To make it more simple, you can think of it as the hard disk data of your computer where all the data gets stored permanently. Similarly, the storage variable is stored in the state of a smart contract and is persistent between function calls. Keep in mind that storage data location is expensive compared to other data locations. 
 

Memory

 
The memory location is temporary data and cheaper than the storage location. It can only be accessible within the function. Usually, Memory data is used to save temporary variables for calculation during function execution. Once the function gets executed, its contents are discarded. You can think of it as a RAM of each individual function.
 

Calldata

 
Calldata is non-modifiable and non-persistent data location where all the passing values to the function are stored. Also, Calldata is the default location of parameters (not return parameters) of external functions.
 

Stack

Stack is a non-persistent data maintained by EVM (Ethereum Virtual Machine). EVM uses stack data location to load the variables during execution. Stack location has the limitation up to 1024 levels.

There are certain default rules in data locations
 
Rule 1
 
State variables are always stored in the storage.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract DataLocation {  
  4.      
  5.    //storage     
  6.    uint stateVariable;  
  7.    uint[] stateArray;  
  8. }  
Also, you can not explicitly override the location of state variables. 
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract DataLocation {  
  4.      
  5.    uint storage stateVariable; // error  
  6.    uint[] memory stateArray; // error  
  7. }  
Rule 2
 
Function parameters including return parameters are stored in the memory.
 
 Function parameters including return parameters are stored in the memory.
 
Rule 3
 
Local variables with a value type are stored in the memory. However, for a reference type, you need to specify the data location explicitly.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Locations {  
  4.   
  5.   /* these all are state variables  */  
  6.     
  7.   //stored in the storage  
  8.   bool flag;  
  9.   uint number;  
  10.   address account;  
  11.   
  12.   function doSomething() public  {  
  13.     
  14.     /* these all are local variables  */  
  15.       
  16.     //value types  
  17.     //so they are stored in the memory  
  18.     bool flag2;  
  19.     uint number2;  
  20.     address account2;  
  21.           
  22.     //reference type  
  23.     uint[] memory localArray;        
  24.   }  
  25. }     
Local variables with value types cannot be overriden explicitly. 
  1. function doSomething() public  {  
  2.     
  3.     /* these all are local variables  */  
  4.       
  5.     bool memory flag2; //error   
  6.     uint Storage number2; // error  
  7.     address account2;                  
  8.   }  
Rule 4
 
Function parameters (not including returns parameters) of external function are stored in the Calldata.

Default Copy Behavior 

Data can be copied from one to another variable in two ways. One way is to copy the entire data (copy by value) and the second way is to copy by reference. 
 
There are certain default behaviors of copy data location from one to another.  
 
Rule 5
 
Assignment of one state variable to another state variable creates a new copy.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Locations {  
  4.   
  5.   uint public stateVar1 = 10;  
  6.   uint stateVar2 = 20;  
  7.   
  8.   function doSomething() public returns (uint) {  
  9.      
  10.    stateVar1 = stateVar2;  
  11.    stateVar2 = 30;  
  12.      
  13.    return stateVar1; //returns 20  
  14.   }  
  15. }   

Here, in this example, stateVar1 and stateVar2 are the state variables. In doSomething function, we’re copying the value of stateVar2 to the stateVar1; now the value of stateVar1 would be 20. But to make sure it's creating a new copy, we have changed the value of stateVar2. So, if they would not create a copy, then the value of stateVar1 should be 30. However, as it’s creating a new copy, you will get 20 in return.

Datalocation-of-State-to-State-variable
 
That same applies to reference type state variables.
 
Rule 6 
 
Assignment to storage state variable from a memory variable always creates a new copy.
  1. pragma solidity ^ 0.5.0;
  2.   
  3. contract Locations {
  4.   
  5.     uint stateVar = 10; //storage
  6.     
  7.     function doSomething() public returns(uint) {
  8.   
  9.         uint localVar = 20; //memory    
  10.         stateVar = localVar;  
  11.         localVar = 40;
  12.   
  13.         return stateVar; //returns 20    
  14.     }  
  15. }  
Here, in the above example, we’ve one state variable and one local variable. In the function, we’re assigning the value of the local variable to the state variable. To check the behavior, we’ve changed the value of our local variable; and returned the value of the state variable. Here you can see that it's returning 20 whether it's created a new copy or not.
 
Rule 7
 
Assignment to a memory variable from state storage variable will create a copy.
  1. pragma solidity ^ 0.5.0;
  2.   
  3. contract Locations {
  4.   
  5.     uint stateVar = 10; //storage
  6.     
  7.     function doSomething() public returns(uint) {
  8.   
  9.         uint localVar = 20; //memory    

  10.         localVar = stateVar;  
  11.         stateVar = 40;  

  12.         return localVar; //returns 10    
  13.     }  
Here, we have assigned the value of the state variable to the local variable and changing the value of a state variable. Now, to check if the value of our local variable has changed or not, we’ve returned it. Here, localVar will have value 10 as its creating individual copy.
 
Rule 8
 
Assignment from one memory variable to another memory variable will not create a copy. This is applicable to reference type variables only. Local variable still creates a new copy.
 
Reference Type
  1. pragma solidity ^ 0.5.0;
  2.   
  3. contract Locations {  

  4.     function doSomething() 
  5.         public pure returns(uint[] memory, uint[] memory) {
  6.   
  7.         uint[] memory localMemoryArray1 = new uint[](3);  
  8.         localMemoryArray1[0] = 4;  
  9.         localMemoryArray1[1] = 5;  
  10.         localMemoryArray1[2] = 6;
  11.   
  12.         uint[] memory localMemoryArray2 = localMemoryArray1;  
  13.         localMemoryArray1[0] = 10;
  14.   
  15.         return (localMemoryArray1, localMemoryArray2); 
  16.        //returns 10,4,6 | 10,4,6    
  17.     }  
  18. }  

In the above example, we have initialized one array variable in memory with the name localMemoryArray1 and assigned the values 4,5 and 6. Then, we copied that variable to the new memory variable called localMemoryArray2. Then, we have modified the value of the first index in localMemoryArray1 and returned both the arrays. This will give the same result as they both pointed at the same location.

Let’s check the same with a value type. The following function will create a new copy and return 20.
 
Value Type 
  1. pragma solidity ^ 0.5.0;  

  2. contract Locations {  

  3.     function doSomething() public pure returns(uint) {
  4.   
  5.         uint localVar1 = 10; //memory    
  6.         uint localVar2 = 20; //memory
  7.     
  8.         localVar1 = localVar2;  
  9.         localVar2 = 40;
  10.   
  11.         return localVar1; //returns 20    
  12.     }  
  13. }  
In this article, we have discussed different data locations - storage, memory, calldata, and stack. We've also covered default rules. In our next article, we will explore reference types in Solidity.