Closures

Closures ou expressões lambda têm tipos que não podem ser nomeados. No entanto, eles implementam os traits especiais Fn, FnMut e FnOnce:

fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {
    println!("Calling function on {input}");
    func(input)
}

fn main() {
    let add_3 = |x| x + 3;
    println!("add_3: {}", apply_with_log(add_3, 10));
    println!("add_3: {}", apply_with_log(add_3, 20));

    let mut v = Vec::new();
    let mut accumulate = |x: i32| {
        v.push(x);
        v.iter().sum::<i32>()
    };
    println!("accumulate: {}", apply_with_log(&mut accumulate, 4));
    println!("accumulate: {}", apply_with_log(&mut accumulate, 5));

    let multiply_sum = |x| x * v.into_iter().sum::<i32>();
    println!("multiply_sum: {}", apply_with_log(multiply_sum, 3));
}

Um Fn não consome nem muda os valores capturados ou talvez não capture nada, então, pode ser chamado várias vezes simultaneamente.

Um FnMut pode alterar os valores capturados, então você pode chamá-lo várias vezes, mas não simultaneamente.

Se você tiver um FnOnce, poderá chamá-lo apenas uma vez. Pode consumir os valores capturados.

FnMut é um subtipo de FnOnce. Fn é um subtipo de FnMut e FnOnce. Ou seja você pode usar um FnMut sempre que um FnOnce é chamado e você pode usar um Fn sempre que um FnMut ou um FnOnce é chamado.

The compiler also infers Copy (e.g. for add_3) and Clone (e.g. multiply_sum), depending on what the closure captures.

By default, closures will capture by reference if they can. The move keyword makes them capture by value.

fn make_greeter(prefix: String) -> impl Fn(&str) {
    return move |name| println!("{} {}", prefix, name)
}

fn main() {
    let hi = make_greeter("Hi".to_string());
    hi("there");
}