If / Else / For / While Loops
If Statements
If statements in Solidity look just like javascript.
function eatBLT(string memory sandwich) public {
// Remember with strings, we have to compare their keccak256 hashes
// to check equality
if (keccak256(abi.encodePacked(sandwich)) == keccak256(abi.encodePacked("BLT"))) {
eat();
}
}
Example - If / Else
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract IfElse {
function foo(uint x) public pure returns (uint) {
if (x < 10) {
return 0;
} else if (x < 20) {
return 1;
} else {
return 2;
}
}
function ternary(uint _x) public pure returns (uint) {
// if (_x < 10) {
// return 1;
// }
// return 2;
// shorthand way to write if / else statement
// the "?" operator is called the ternary operator
return _x < 10 ? 1 : 2;
}
}
For Loops
Sometimes you'll want to use a for loop to build the contents of an array in a function rather than simply saving that array to storage.
For our getZombiesByOwner
function, a naive implementation would be to store a mapping
of owners to zombie armies in the ZombieFactory
contract:
mapping (address => uint[]) public ownerToZombies
Then every time we create a new zombie, we would simply use ownerToZombies[owner].push(zombieId)
to add it to that owner's zombies array. And getZombiesByOwner
would be a very straightforward function:
function getZombiesByOwner(address _owner) external view returns (uint[] memory) {
return ownerToZombies[_owner];
}
The problem with this approach
This approach is tempting for its simplicity. But let's look at what happens if we later add a function to transfer a zombie from one owner to another. That transfer function would need to:
Push the zombie to the new owner's
ownerToZombies
array,Remove the zombie from the old owner's
ownerToZombies
array,Shift every zombie in the older owner's array up one place to fill the hole, and then
Reduce the array length by 1.
Step 3 would be extremely expensive gas-wise, since we'd have to do a write for every zombie whose position we shifted. If an owner has 20 zombies and trades away the first one, we would have to do 19 writes to maintain the order of the array.
Since writing to storage is one of the most expensive operations in Solidity, every call to this transfer function would be extremely expensive gas-wise. And worse, it would cost a different amount of gas each time it's called, depending on how many zombies the user has in their army and the index of the zombie being traded. So the user wouldn't know how much gas to send.hin
Since view functions don't cost gas when called externally, we can simply use a for-loop in getZombiesByOwner
to iterate the entire zombies array and build an array of the zombies that belong to this specific owner. Then our transfer
function will be much cheaper, since we don't need to reorder any arrays in storage, and somewhat counter-intuitively this approach is cheaper overall.
Using for loops
The syntax of
for
loops in Solidity is similar to JavaScript.Solidity supports
for
,while
, anddo while
loops.Don't write loops that are unbounded as this can hit the gas limit, causing your transaction to fail.
For the reason above, while and do while loops are rarely used.
Let's look at an example where we want to make an array of even numbers:
function getEvens() pure external returns(uint[] memory) {
uint[] memory evens = new uint[](5);
// Keep track of the index in the new array:
uint counter = 0;
// Iterate 1 through 10 with a for loop:
for (uint i = 1; i <= 10; i++) {
// If `i` is even...
if (i % 2 == 0) {
// Add it to our array
evens[counter] = i;
// Increment counter to the next empty index in `evens`:
counter++;
}
}
return evens;
}
This function will return an array with the contents [2, 4, 6, 8, 10]
.
Example - For and While Loop
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Loop {
function loop() public {
// for loop
for (uint i = 0; i < 10; i++) {
if (i == 3) {
// Skip to next iteration with continue
continue;
}
if (i == 5) {
// Exit loop with break
break;
}
}
// while loop
uint j;
while (j < 10) {
j++;
}
}
}
Last updated