Lesson 7 of 23
In Progress

Functions and Control Structures

Solidity is a programming language that is used to create smart contracts on the Ethereum platform. In this article, we will be covering functions and control structures in Solidity, which are essential concepts in any programming language.

Functions

A function is a block of code that performs a specific task. Functions in Solidity are defined using the function keyword, followed by the function name, and a set of parentheses containing any input parameters. The function body is then defined within curly braces. Here is an example of a simple function that adds two numbers together:

function addNumbers(uint a, uint b) public pure returns (uint) {
    return a + b;
}

In this example, the function is named addNumbers, it takes in two input parameters of type uint, and it returns a single value of type uint. The public and pure keywords specify the visibility and purity of the function, respectively.

Functions can also have an optional returns clause, which specifies the type and number of values that the function will return. In the example above, the returns clause specifies that the function will return a single value of type uint.

Control Structures

Control structures allow you to control the flow of execution in your code. Solidity supports the following control structures:

  • if statements
  • for loops
  • while loops

if statements

An if statement allows you to execute a block of code only if a certain condition is met. The syntax for an if statement in Solidity is as follows:

if (condition) {
    // code to be executed if condition is true
}

You can also add an optional else clause to execute a block of code if the condition is not met:

if (condition) {
    // code to be executed if condition is true
} else {
    // code to be executed if condition is false
}

Here is an example of an if statement in Solidity:

function checkNumber(uint x) public pure returns (bool) {
    if (x > 10) {
        return true;
    } else {
        return false;
    }
}

In this example, the function checkNumber takes in a single input parameter x of type uint. It then checks if x is greater than 10. If it is, the function returns true, otherwise it returns false.

for loops

A for loop allows you to execute a block of code multiple times. The syntax for a for loop in Solidity is as follows:

for (initialization; condition; increment) {
    // code to be executed
}

The initialization clause is executed once at the beginning of the loop. The condition clause is checked before each iteration of the loop. If the condition is true, the loop continues to execute. If the condition is false, the loop terminates. The increment clause is executed after each iteration of the loop.

Here is an example of a for loop that iterates 10 times:

pragma solidity ^0.6.0;

contract ForLoopExample {
    uint public sum;

    function forLoopExample() public {
        for (uint i = 0; i < 10; i++) {
            sum += i;
        }
    }
}

In this example, the loop counter i is initialized to 0 at the beginning of the loop. The condition i < 10 is evaluated at the beginning of each iteration. As long as i is less than 10, the code block within the loop will be executed. The loop counter i is incremented by 1 at the end of each iteration. The loop will terminate once i becomes equal to 10.

It is important to note that the loop counter must be of type uint or int. If the loop counter is of type uint8, for example, the loop will not execute more than 255 times due to the maximum value of a uint8 being 255.

In addition to the standard for loop, Solidity also supports the for...in loop, which is used to iterate over the elements in an array. The syntax for a for...in loop is as follows:

for (variable in array) {
    // code block to be executed
}

The variable represents the current element being iterated over in the array. The loop will terminate once all elements in the array have been iterated over.

Here is an example of a for...in loop that iterates over an array of integers:

pragma solidity ^0.6.0;

contract ForInLoopExample {
    uint[] public numbers;

    function forInLoopExample() public {
        numbers = [1, 2, 3, 4, 5];
        for (uint number in numbers) {
            // code block to be executed
        }
    }
}

In this example, the for...in loop will iterate over each element in the numbers array. The number variable will take on the value of each element in the array as the loop iterates.

It is important to note that the for...in loop can only be used with arrays of fixed size. It cannot be used with dynamic arrays or mappings.

While Loops

While loops are used to execute a block of code repeatedly until a certain condition is met. The syntax for a while loop in Solidity is as follows:

while (condition) {
    // code block to be executed
}

The code block will be executed as long as the condition is true. It is important to make sure that the condition eventually becomes false, or else the loop will run indefinitely, leading to an infinite loop.

Here is an example of a while loop that counts down from 10 to 1:

pragma solidity ^0.6.0;

contract WhileLoopExample {
    uint public counter;

    constructor() public {
        counter = 10;
    }

    function countDown() public {
        while (counter > 0) {
            counter--;
        }
    }
}

In this example, the countDown function will decrement the counter variable by 1 until it reaches 0.

It is also possible to use a break statement to exit a while loop early. For example:

pragma solidity ^0.6.0;

contract WhileLoopExample {
    uint public counter;

    constructor() public {
        counter = 10;
    }

    function countDown() public {
        while (true) {
            counter--;
            if (counter == 0) {
                break;
            }
        }
    }
}

In this version of the countDown function, the loop will run indefinitely because the condition is always true. However, the break statement will cause the loop to exit once the counter variable reaches 0.

It is also possible to use a continue statement to skip the rest of the current iteration of a loop and move on to the next iteration. For example:

pragma solidity ^0.6.0;

contract WhileLoopExample {
    uint public counter;

    constructor() public {
        counter = 10;
    }

    function countDown() public {
        while (counter > 0) {
            counter--;
            if (counter % 2 == 0) {
                continue;
            }
            // code to be executed if counter is odd
        }
    }
}

In this version of the countDown function, the continue statement will cause the loop to skip over any iterations where the counter variable is even and move on to the next iteration. This means that the code block following the continue statement will only be executed for odd values of counter.

It is important to use control structures like for loops and while loops appropriately in your smart contracts to ensure that they behave as intended and do not consume too many resources.

There are several other important control structures that can be used in Solidity to help you create more complex and powerful smart contracts. These include:

Break and Continue Statements

Just like in other programming languages, the break and continue statements can be used within looping structures to alter the flow of control. The break statement will exit the loop entirely, while the continue statement will skip the remainder of the current iteration and move on to the next one.

For example, consider the following code snippet:

uint i = 0;
while (true) {
    i++;
    if (i >= 10) {
        break;
    } else if (i % 2 == 0) {
        continue;
    }
    // Only odd numbers will be printed
    print(i);
}

This code will print the numbers 1, 3, 5, 7, and 9, since the continue statement will skip over any even numbers and the break statement will exit the loop once i reaches 10.

Do-While Loops

The do-while loop is similar to the while loop, but the condition is checked at the end of the loop rather than at the beginning. This means that the loop will always be executed at least once, even if the condition is initially false.

For example:

uint i = 0;
do {
    i++;
    print(i);
} while (i < 10);

This code will print the numbers 1 through 10, since the condition i < 10 is checked at the end of the loop rather than at the beginning.

Switch Statements

The switch statement allows you to execute different code blocks based on the value of a particular expression. It is often used as an alternative to a series of if-else statements, especially when there are many possible cases to consider.

For example:

uint x = 3;
switch (x) {
    case 1:
        // Code to execute if x is 1
        break;
    case 2:
        // Code to execute if x is 2
        break;
    case 3:
        // Code to execute if x is 3
        break;
    default:
        // Code to execute if x is none of the above
        break;
}

In this example, the code block for case 3 will be executed, since x has a value of 3. If x had any other value, the code in the default block would be executed.

Try-Catch Blocks

The try-catch block is a control structure that allows you to handle exceptions, which are runtime errors that can occur during the execution of your code. The try block contains the code that may throw an exception, and the catch block contains the code to handle the exception if it is thrown.

For example:

try {
    // Code that may throw an exception
} catch (ExceptionType e) {
    // Code to handle the exception
}

If an exception is thrown in the try block, execution will immediately jump to the catch block, where you can handle the exception as needed.

Conclusion

In conclusion, functions and control structures are an essential part of any programming language, and Solidity is no exception. By understanding how to use functions, if statements, for loops, and while loops, you will be able to create more complex and powerful smart contracts. It is important to keep in mind the potential pitfalls of using these control structures, such as the possibility of infinite loops and the need to properly handle exceptions and failures. With practice and a thorough understanding of these concepts, you will be well on your way to writing secure and efficient smart contracts.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Write a Solidity function that takes in a uint value and returns the factorial of that value using a for loop.

pragma solidity ^0.6.0;

contract Factorial {
    function factorial(uint value) public view returns (uint) {
        uint result = 1;
        for (uint i = 2; i <= value; i++) {
            result = result.mul(i);
        }
        return result;
    }
}

Write a Solidity function that takes in an array of uint values and returns the sum of all the values using a while loop.

pragma solidity ^0.6.0;

contract Sum {
    function sum(uint[] memory values) public view returns (uint) {
        uint result = 0;
        uint i = 0;
        while (i < values.length) {
            result += values[i];
            i++;
        }
        return result;
    }
}

Write a Solidity function that takes in a uint value and returns the nth number in the Fibonacci sequence using a for loop.

pragma solidity ^0.6.0;

contract Fibonacci {
    function fibonacci(uint value) public view returns (uint) {
        uint a = 0;
        uint b = 1;
        for (uint i = 2; i <= value; i++) {
            uint c = a.add(b);
            a = b;
            b = c;
        }
        return b;
    }
}

Write a Solidity function that takes in a string and returns the string with all vowels removed using a while loop.

pragma solidity ^0.6.0;

contract RemoveVowels {
    function removeVowels(string memory value) public view returns (string memory) {
        bytes memory result = new bytes(value.length);
        uint i = 0;
        uint j = 0;
        while (i < value.length) {
            if (value[i] != 'a' && value[i] != 'e' && value[i] != 'i' && value[i] != 'o' && value[i] != 'u') {
                result[j] = value[i];
                j++;
            }
            i++;
        }
        return string(result[0:j]);
    }
}

Write a Solidity function that takes in a mapping and returns the keys of the mapping in an array using a for loop.

pragma solidity ^0.6.0;

contract GetKeys {
    mapping(uint => string) public values;

    function getKeys() public view returns (uint[] memory) {
        uint[] memory keys = new uint[](values.length);
        uint i = 0;
        for (uint key in values) {
            keys[i] = key;
            i++;
        }
        return keys;
    }
}