Reference Types In Solidity

The Solidity data types can be classified according to the data location. If a variable stores its own data; it is a value type. If it holds a location of the data; it is a reference type. We have gone through value types in previous articles. So, in this article, we will learn about reference types.

The Solidity data types can be classified according to the data location. If variable store its own data; it is a value type. If it holds a location of the data; it is a reference type. We have gone through value types in previous articles. So, in this article, we will learn about reference types.
 
As discussed, reference types do not store the data directly to the variable, instead, it stores the location of the data. With a reference type, two different variables can reference the same location, in such a case; any change in one variable will affect to another variable; therefore value types need to be handled very carefully.
 
Since the version 0.5.0 of Solidity, for all complex types, you need to define data location explicitly with a variable.
 
Reference types consist of,
  • Arrays
  • Structs
  • Mapping
The main difference between the value type and reference type is Data location. Arrays and Structs have additional data location which specifies where data (value of the variable) should be stored.
 
Learn more about data location in detail:  Data Locations In Solidity 
 

Arrays

 
Arrays are the groups of variables of the same type in which each individual variable has a particular location called index. By using that index location, you can access that variable. The size of arrays can be fixed size or dynamic.
 
Fixed Size Arrays
 
Fixed size arrays have pre-defined size while declaration. For example, an array of fixed size 5 and element type uint is written as,
  1. uint[5] arrayName;  
When we declare an array, it doesn’t initialize; we have to initialize it with a new keyword. However, in Solidity you cannot use a new keyword with a fixed-size array; instead, they can be initialized inline.
  1. uint[5] marks  = [uint(50), 60,70,80,90];  
Example
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.       
  5.     function getMarks() public pure returns (uint[5] memory){  
  6.           
  7.         uint[5] memory marks  = [uint(50), 60, 70, 80, 90];  
  8.           
  9.         return marks;  
  10.   }  
  11. }  
  12. //result  
  13. //50,60,70,80,90  
Note
In the example above, the type of [50, 60, 70, 80, 90] is uint8[5] memory. Because the type of each individual value is uint8; if you want the result in uint[5] memory, you need to convert the first element to uint.
 
Or you can declare an array as a state variable and initialize in a function
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.       
  5.     uint[5] marks;    
  6.       
  7.     function getMarks() public returns (uint[5]memory){  
  8.       
  9.         marks  = [uint(50), 60,70,80,90];  
  10.           
  11.         return marks;  
  12.   }  
  13. }  
  14. //result  
  15. //50,60,70,80,90  

Dynamic Arrays

Dynamic arrays don't have predefined size at the time of declaration; the size then will be determined at a run time.
 
Declaration
  1. uint[] arrayName;  
You can initialize a dynamic array with "new" keyword or inline.
  1. uint[5] mathMarks = [uint(50), 60,70,80,90];
  2.   
  3. int[] scienceMarks = new int[](5);  
Example
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     uint[] mathMarks = [10, 20, 30, 40, 50]; //inline  
  6.     uint[] scienceMarks;  
  7.   
  8.     function getMarks() public returns(uint[] memory, uint[] memory){  
  9.   
  10.         scienceMarks = [60, 70, 80, 90, 100]; // inline  
  11.   
  12.         return (mathMarks, scienceMarks);  
  13.     }  
  14. }  
  15. //result  
  16. //"0": "uint256[]: 10,20,30,40,50",   
  17. //"1": "uint256[]: 60,70,80,90,100"  
With using new keyword
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.       
  5.     uint[] scienceMarks;  
  6.       
  7.     function getMarks() public returns (uint[]memory){  
  8.        
  9.      scienceMarks = new uint[](5); // using new keyword  
  10.      scienceMarks[0] = 10;  
  11.      scienceMarks[1] = 20;  
  12.      scienceMarks[2] = 30;  
  13.      scienceMarks[3] = 40;  
  14.      scienceMarks[4] = 50;  
  15.        
  16.     return (scienceMarks);  
  17.   }  
  18. }  
  19. //result  
  20. //"0": "uint256[]: 10,20,30,40,50"  
However, a memory array with a fixed size cannot be assigned to dynamically-sized memory arrays.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     function getMarks() public returns(uint[] memory) {  
  6.   
  7.          uint[] memory scienceMarks = [uint(60), 70, 80, 90, 100]; // error  
  8.   
  9.          return scienceMarks;  
  10.     }  
  11. }  

Array Members

 
length
 
Length member is used to getting a count of elements that array contains. The size of a memory array is fixed once they are created, but the length of dynamic-sized arrays can be defined at runtime; in this case length member is used to resize the array with the storage location.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     uint[] array;     
  6.   
  7.     //passing _length = 3    
  8.     function getLength(uint _length) public returns(uint) {  
  9.   
  10.         array.length = _length;  
  11.           
  12.         return array.length; // return 3  
  13.     }  
  14. }  
push
 
Dynamic storage arrays have a member called push; that is used to add an element to the array at the last position.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     //storage variable  
  6.     uint[] arr;       
  7.   
  8.     function insert() public returns(uint[] memory) {  
  9.           
  10.         arr = [1, 2, 3];  
  11.           
  12.         //pushing values to the array  
  13.         arr.push(4);  
  14.         arr.push(5);  
  15.         arr.push(6);  
  16.           
  17.         return arr; // return 1,2,3,4,5,6  
  18.     }  
  19. }  
pop
 
Dynamic storage arrays have a member called pop that you can use to remove the last element from the array.
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     uint[] array;     
  6.   
  7.     function removeLastOne() public returns(uint[] memory) {  
  8.           
  9.         array = [1, 2, 3];  
  10.         array.pop();  
  11.           
  12.         return array; // return 1,2  
  13.     }  
  14. }  

Special Arrays

In Solidity, bytes and string are special arrays.
 
Bytes array
 
The bytes array has dynamic size and is declared using "bytes" keyword; or it can be initialized in function as follows:
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     bytes byteArray1 = new bytes(5);   
  6.     bytes byteArray2;  
  7.   
  8.     function initializeByteArray() public{  
  9.           
  10.         byteArray2 = new bytes(5);   
  11.     }  
  12. }  
We can assign the value directly to the bytes array:
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     bytes byteArray2;  
  6.   
  7.     function assignValue() public{  
  8.           
  9.         byteArray2 = "C# Corner";  
  10.     }  
  11.       
  12.     function getValue() public view returns (bytes memory){  
  13.         return  byteArray2;  
  14.     }  
  15. }  
The bytes array supports length, push and pop members:
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     bytes byteArray2;  
  6.   
  7.     function assignValue() public{  
  8.           
  9.         byteArray2 = "C# Corner";  
  10.         byteArray2.push('-');  
  11.         byteArray2.pop();  
  12.     }  
  13.       
  14.     function getLength() public view returns (uint){  
  15.         return  byteArray2.length;  
  16.     }  
  17. }  
String array
 
String is also a dynamic array just like bytes, but with special constraints. Unlike other arrays, string doesn't have the index, thus string doesn't contain array members -- length, push and pop.
 
Example
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     string firstName;  
  6.   
  7.     function assignValue() public returns (string memory, string memory){  
  8.           
  9.       firstName = "C#";  
  10.         
  11.       string memory secondName = "Corner";  
  12.         
  13.       return (firstName, secondName);  
  14.     }  
  15. }   

Structs

 
Solidity enables users to create their own type in the form of Structure. The structure is the group of different types although it is not possible to contain a member of its own type. Structure is a reference type variable and can contain both - value types and reference types. 
 
Declaration
  1. struct <name of structure> {  
  2.    <type> variable1;  
  3.    <type> variable2;  
Example 
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract Types {  
  4.   
  5.     struct User {  
  6.         string name;  
  7.         uint age;  
  8.         bool isValid;  
  9.     }  
  10.       
  11.     User _user = User("John", 40, true);  
  12.       
  13.     function getUserInfo()   
  14.              public view   
  15.              returns (string memory, uint,bool) {  
  16.           
  17.         return(_user.name, _user.age, _user.isValid);  
  18.     }  
  19. }  
  20. //result  
  21. //John, 40, true  
Note that, assignment of a local variable to the structure with storage location creates a reference.
 

Mapping Types

 
Mapping types are the most used reference type; they are used to store data in a key-value pair; where the key can be any built-in value types or byte and string. You can think of it as a hash table or dictionary as in any other language, in which a user can store data in a key-value format and data can be retrieved by key.
 
Declaration
  1. mapping(_KeyType => _ValueType) <access specifier> <name>;  
Example
  1. mapping (address => uint) account;  
Here, the address is used to store the information of key, and uint type is used to store information of value. "account" is the identifier; by using identifier we can access mapping later in our contract.
 
Example of storing and retrieving user information using mapping:
  1. pragma solidity ^0.5.0;  
  2.   
  3. contract MappingExample {  
  4.   
  5.     mapping(address => uint) account;  
  6.   
  7.     function updateBalance(uint newBalance) public {  
  8.         account[msg.sender] = newBalance;  
  9.     }  
  10.       
  11.     function getBalance(address _address) 
  12.             public view returns (uint) {
  13.   
  14.         return account[_address];  
  15.     }  
  16. }  
Once you deploy the above code over Remix, you will see the screen as we are having two methods.
 
Reference Types In Solidity
 
The first method is for saving the balance as value and the address of the user as a key, and in the second method we're retrieving the value of the balance we stored on address key. Let's run it and see the output.
 
 Reference Types In Solidity
And we're able to see the value that we've stored in mapping!
 
In this article, we have discussed different reference types - arrays, structures, mappings. We’ve also covered the usage of all. In our next article, we will learn constructor in Solidity.
 
Reference
  • https://solidity.readthedocs.io/en/v0.5.0/