ruk·si

🦀 Rust
Functions

Updated at 2024-01-04 06:26

Functions always have a fixed number of parameters. Macros can take a variable number of parameters if you really need that e.g. format!().

Not ending your code block with ; will return the result of that block.

fn number() -> i32 {
    8
}

fn number() -> i32 {
    return 8;
}

It is common to use control structures that implicitly return the response.

fn main() {
    assert_eq!(dice_roll_conditional(true), 6);
    assert_eq!(dice_roll_conditional(false), 4);
    assert_eq!(dice_roll_match(true), 6);
    assert_eq!(dice_roll_match(false), 4);
}

fn dice_roll_conditional(feeling_lucky: bool) -> i32 {
    if feeling_lucky {
        6
    } else {
        4
    }
}

fn dice_roll_match(feeling_lucky: bool) -> i32 {
    match feeling_lucky {
        true => 6,
        false => 4,
    }
}

Closures

Vertical pipes || start a closure.

let sleepy = |num| {
    thread::sleep(Duration::from_secs(num));
    num
};

You can take ownership of used context variables with the move prefix.

They don't need curly braces unless you want to have multiple statements.

let x = String::from("hello");
let equal_to_x = move |z| z == x;
// x is moved into the closure and not owned by the context anymore

Closures implement closure traits Fn, FnMut or FnOnce with some captured context.

  • Fn: can be called repeatedly and doesn't modify the captured context
  • FnMut: can be called repeatedly and can modify the captured context
  • FnOnce: can only be called once and consumes the captured context
fn for_each_planet<F: Fn(&'static str)>(f: F) {
    f("Earth");
    f("Mars");
    f("Jupiter");
}

fn main() {
    // here the `|planet| ...` is a closure of type `Fn`
    for_each_planet(|planet| println!("Hello, {}", planet));
}

As closures are traits, you must use trait object to return then.

// does not compile
fn returns_closure() -> Fn(i32) -> i32 { |x| x + 1 }

// boxed closure
fn returns_closure() -> Box<Fn(i32) -> i32> { |x| x + 1 }

// a more modern way
fn returns_closure() -> impl Fn(i32) -> i32 { |x| x + 1 }

Function Pointers

Function pointers fn are not closures, but implement all the closure traits.

Here the do_twice function parameter is a function pointer type. Normally you'd use a closure trait for this to allow using either a function pointer or a closure.

fn add_one(x: i32) -> i32 { x + 1 }

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { f(arg) + f(arg) }

fn main() {
    let answer = do_twice(add_one, 5);
    println!("The answer is: {}", answer);
}

Sources