Functions in Rust: a good introduction

Francesco Ciulla - Jan 30 - - Dev Community

Functions in Rust

In this 4th lesson, we will learn about functions in Rust.

If you prefer a video version

What is a function?

A function is a set of statements that performs a task.

Functions are used to organize code into logical blocks, making it easier to read and maintain.

Functions are also used to avoid code duplication.

Basics

we know that the main function is essential in every Rust program. For instance:

fn main() {
    println!("Hello, world!");
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

Let's declare another_function and call it from main.

We use the snake_case naming convention for functions.

fn main() {
    println!("Hello, world!");
    another_function();
}

fn another_function() {
    println!("another function in snake_case");
}
Enter fullscreen mode Exit fullscreen mode

Will it compile?

functions in Rust - Rust programming tutorial

Yes, in Rust, the order of function declarations is not important, allowing flexibility in organizing code.

Parameters

Parameters are special variables part of the function's signature, used to pass data to the function.

Although the terms parameters and arguments are often used interchangeably, technically, parameters refer to the variables in the function's definition, while arguments are the concrete values passed to the function.

Let's add a parameter to our function:

fn main() {
    another_function(42);
}

fn another_function(num) {
    println!("The values is, {}!", num);
}
Enter fullscreen mode Exit fullscreen mode

Will it compile?

functions in Rust - Rust programming tutorial

No. This is because Rust is a statically typed language. This means that the compiler must know the type of every variable at compile time.

Here, the compiler doesn't know the type and it cannot infer it from the function body.

In function signatures, you must declare the type of each parameter.

This is a deliberate decision in Rust’s design: requiring type annotations in function definitions means the compiler almost never needs you to use them elsewhere in the code to figure out what type you mean.

The compiler is also able to give more helpful error messages if it knows what types the function expects.

fn main() {
    function2(42);
}

fn function2(num: i32) {
    println!("The values is, {}!", num);
}

Enter fullscreen mode Exit fullscreen mode

Will it compile?

functions in Rust - Rust programming tutorial

Yes. Now the compiler knows that num is an i32.

Multiple Parameters

When defining multiple parameters, you must separate them with a comma.

fn main() {
    function2(42, 'a');
}

fn function2(num: i32, letter: char) {
    println!("The values are, {} and {}!", num, letter);
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

Statements and Expressions

Understanding the difference between statements and expressions is crucial in Rust:

Statements are instructions that perform some action but do not return a value.

Expressions evaluate to a resulting value.

In Rust, functions can be considered as expressions, meaning they evaluate to a value.

For example, creating a variable is a statement, as it does not return a value:

fn main() {
    let x = 42;
}
Enter fullscreen mode Exit fullscreen mode

Can we do this?

fn main() {
    let x = (let y = 42);
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

No, because let y = 42 is a statement and statements cannot be used as expressions.

Note that this would be valid in other languages, such as C, where the assignment operator returns the value assigned.

Expressions can be used as statements, but they will not return a value.

fn main() {
    let x = 5;
    let y = 6;
    let z = x + y;
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

  • Calling a function is an expression.
  • Calling a macro is an expression.
  • A new scope block created with curly brackets is an expression, for example:
fn main() {
  let y = {
    let x = 3;
    x + 1
  };

  println!("The value of y is: {y}");
}
Enter fullscreen mode Exit fullscreen mode

This block evaluates to 4, which is then assigned to y.
Note the absence of a semicolon in the last expression of the block.

Return Values

Functions can return values to the code that calls them.
We don't name return values, but we do declare their type after an arrow (->).

Example:

fn sum() -> i32 {
    1 + 2
}

fn main() {
    let x = sum();
    println!("The value of x is: {x}");
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

This works because the last expression in the function is 1 + 2, which evaluates to 3, and 3 is returned from the function.

fn sum(num1:i32, num2:i32) -> i32 {
    num1 + num2
}

fn main() {
    let x = sum(5, 7);
    println!("The value of x is: {x}");
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

We can return multiple values by separating them with a comma, and the return type must be a tuple:

//Return values in functions
fn sum_diff(num1:i32, num2:i32) -> (i32, i32) {
    (num1 + num2, num1 - num2)
}

fn main() {
    let x = sum_diff(10,8);
    println!("Sum and Diff is {:?}", x);
}

Enter fullscreen mode Exit fullscreen mode

In this case, we return a tuple containing the sum and the difference of the two numbers.

functions in Rust - Rust programming tutorial

The {:?} is a debug print

Early Returns

Functions can return early with the return keyword.

//Return values in functions
fn sum_diff(num1:i32, num2:i32) -> (i32, i32) {
    return (num1 + num2, num1 - num2);
}

fn main() {
    let x = sum_diff(10,8);
    println!("Sum and Diff is {:?}", x);
}
Enter fullscreen mode Exit fullscreen mode

Functions can return early with the return keyword.

//Return values in functions
fn sum_diff(num1:i32, num2:i32) -> (i32, i32) {
    return (num1 + num2, num1 - num2);
    println!("This line will not be executed");
}

fn main() {
    let x = sum_diff(10,8);
    println!("Sum and Diff is {:?}", x);
}
Enter fullscreen mode Exit fullscreen mode

functions in Rust - Rust programming tutorial

in this case, the last line in the sum_diff function will not be executed.

Recap

To make a recap:

  • they use the fn keyword
  • they are snake_case
  • they can be declared anywhere
  • they can be declared with or without parameters
  • they can return only one type, but it can be any type, including compund and custom types
  • they can return early with the return keyword

If you prefer a video version

You can find me here: Francesco Ciulla

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .