Defining Functions in Rust: Practical Examples

Explore practical examples of defining functions in Rust, showcasing various use cases and best practices.
By Jamie

Introduction to Defining Functions in Rust

In Rust, functions are fundamental building blocks of code that allow you to encapsulate logic and perform tasks. They help in organizing your code, improving readability, and enabling code reuse. Below are three practical examples of defining functions in Rust, each demonstrating different contexts and use cases.

Example 1: Basic Function for Addition

Context

This example illustrates a straightforward function to perform addition of two integers. It’s a fundamental operation that serves as a good entry point for understanding function definition in Rust.

fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let sum = add_numbers(5, 10);
    println!("The sum is: {}", sum);
}

When the main function is executed, it calls add_numbers with the integers 5 and 10, returning 15, which is then printed to the console.

Notes

  • The function signature fn add_numbers(a: i32, b: i32) -> i32 specifies that it takes two parameters of type i32 and returns an i32.
  • You can modify the function to handle different numeric types by using generics if needed.

Example 2: Function with Multiple Return Values

Context

In this example, we define a function that calculates both the area and perimeter of a rectangle given its width and height. This showcases Rust’s capability to return multiple values using tuples.

fn rectangle_metrics(width: f64, height: f64) -> (f64, f64) {
    let area = width * height;
    let perimeter = 2.0 * (width + height);
    (area, perimeter)
}

fn main() {
    let (area, perimeter) = rectangle_metrics(5.0, 10.0);
    println!("Area: {}, Perimeter: {}", area, perimeter);
}

When executed, this code will output the area and perimeter of a rectangle with the given dimensions.

Notes

  • The function returns a tuple (f64, f64) which holds both the area and perimeter values.
  • You can destructure the tuple in the main function for cleaner code.

Example 3: Function with Closures

Context

This example demonstrates defining a function that accepts a closure as a parameter. Closures are often used for callback functions and can provide flexible behavior in your code.

fn apply_function<F>(func: F, value: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    func(value)
}

fn main() {
    let double = |x| x * 2;
    let result = apply_function(double, 5);
    println!("Result of doubling: {}", result);
}

In this case, the apply_function accepts any function or closure that matches the signature, allowing for a versatile implementation.

Notes

  • The function uses a generic type F constrained by the Fn trait, which allows it to accept different callable types.
  • You can easily modify the closure to perform different operations, such as squaring or adding a constant.

These examples provide a foundation for understanding how to define functions in Rust, showcasing their versatility and power in writing efficient and clean code.