In this lesson, you'll learn about fundamental data structures in Solidity: structs, arrays, and mappings. These structures are crucial for organizing and managing data within your smart contracts, enabling you to build more complex and functional decentralized applications.
Data structures are fundamental building blocks for organizing and storing data efficiently. In Solidity, you'll primarily use structs, arrays, and mappings to manage the information your smart contracts interact with. These structures allow you to represent complex data in a structured manner, making your code easier to read, write, and maintain.
Structs allow you to create custom, composite data types that group related variables. Think of them as blueprints for creating objects with specific properties.
Example:
pragma solidity ^0.8.0;
struct Person {
string name;
uint age;
address walletAddress;
}
contract StructExample {
Person public john;
function createPerson(string memory _name, uint _age, address _walletAddress) public {
john = Person(_name, _age, _walletAddress);
}
function getPersonName() public view returns (string memory) {
return john.name;
}
}
In this example, Person
is a struct containing a name
, age
, and walletAddress
. We then declare a john
variable of type Person
and a function to initialize the struct.
Arrays in Solidity are ordered collections of elements of the same data type. You can have fixed-size arrays (size defined at compile time) and dynamic arrays (size can change at runtime).
Fixed-Size Array Example:
pragma solidity ^0.8.0;
contract FixedArrayExample {
uint[3] public numbers;
function setNumbers() public {
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
}
function getNumberAtIndex(uint _index) public view returns (uint) {
return numbers[_index];
}
}
Dynamic Array Example:
pragma solidity ^0.8.0;
contract DynamicArrayExample {
uint[] public numbers;
function addNumber(uint _number) public {
numbers.push(_number);
}
function getArrayLength() public view returns (uint) {
return numbers.length;
}
}
In this example, the numbers
array is dynamically sized, and we use the push()
function to add elements. Dynamic arrays use .length
property to find the array's length.
Mappings are like dictionaries or hash tables. They associate a key with a value. The key can be any primitive data type (e.g., uint
, address
, string
) or a bytes
type, and the value can be any data type, including structs and arrays.
Example:
pragma solidity ^0.8.0;
contract MappingExample {
mapping(address => uint) public balances;
function setBalance(address _account, uint _amount) public {
balances[_account] = _amount;
}
function getBalance(address _account) public view returns (uint) {
return balances[_account];
}
}
In this example, balances
is a mapping where the key is an address
(representing an account) and the value is a uint
(representing the balance). Mappings, unlike arrays, don't have a .length
property. Also, mappings are considered initialized for all possible keys. If you try to read a value from a non-existent key, the default value for the value's type will be returned (0 for uint, false for bool, etc.).
When working with data structures, consider these best practices:
Explore advanced insights, examples, and bonus exercises to deepen understanding.
Welcome back! Today, we're going beyond the basics of structs, arrays, and mappings in Solidity. We'll explore their nuances, discover some alternative approaches, and see how they are essential to building practical decentralized applications. This lesson expands on the foundation you've built, preparing you to tackle more intricate smart contract logic.
Let's refine our understanding of each data structure:
While structs are fundamental for creating custom data types, consider them as blueprints for complex objects. Think about adding public
or private
visibility modifiers to the struct members, controlling how the data is accessed and modified. You could even include a method inside the contract, that calls upon the struct, and modifies it's attributes, such as updating an owner of an asset stored inside a struct.
Remember that dynamic arrays can grow and shrink, while fixed-size arrays have a predetermined length. But did you know that Solidity stores data differently? Data can be stored in memory or in storage. Arrays declared locally within a function typically reside in memory (volatile and lost when the function ends), while arrays declared as state variables (outside functions) reside in storage (persistent and stored on the blockchain). Be mindful of where you declare your arrays, because storage is more expensive to use than memory. Using memory is often cheaper from a gas perspective. Be aware of the gas costs involved in iterating through large arrays in storage versus memory.
Mappings are like dictionaries. They're super-efficient for looking up values based on keys. However, they have some limitations. You cannot iterate over a mapping. This means you can't easily retrieve all keys or all values in a mapping. If you need to iterate, consider using an array alongside your mapping, where the array stores the keys. Further, mappings cannot store mappings. Think of alternate approaches for situations where you would need multiple nested mappings, since nesting mappings is not possible.
These data structures are foundational in many real-world Web3 applications:
Create a more complex NFT contract. Enhance your asset-tracking contract from the Bonus Exercises, and add a mechanism to transfer ownership of an NFT from one address to another and also implement a function that allows the owner of the contract to mint NFTs.
Define a struct named `Book` with the following fields: `title` (string), `author` (string), and `pages` (uint). Then, create a contract that uses this struct and allows you to create a book and retrieve its title.
Create a contract with a dynamic array of `address`. Implement functions to add an address to the array and retrieve an address at a specific index.
Create a contract with a mapping that stores the balances of addresses. Implement functions to set the balance of an address and get the balance of an address.
Think about how structs, arrays, and mappings could be used to implement features in different types of dApps (e.g., a simple NFT marketplace, a voting system, a token transfer contract). Describe potential use-cases for each data structure.
Develop a simple NFT (Non-Fungible Token) contract. You can use a struct to represent an NFT with properties like `tokenId`, `name`, and `description`. Use a mapping to track the owner of each NFT based on the `tokenId`, and another to store NFT metadata.
Review the concepts covered in this lesson. In the next lesson, we will explore control structures like conditional statements (if/else) and loops (for/while).
We're automatically tracking your progress. Sign up for free to keep your learning paths forever and unlock advanced features like detailed analytics and personalized recommendations.