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!("Chamando a função com {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));
}
This slide should take about 20 minutes.

Um Fn (p.ex. add_3) nĂŁo consome nem muda os valores capturados ou talvez nĂŁo capture nada, podendo entĂŁo ser chamado vĂĄrias vezes simultaneamente.

Um FnMut (p.ex. accumulate) pode alterar os valores capturados, logo vocĂȘ pode chamĂĄ-lo vĂĄrias vezes, mas nĂŁo simultaneamente.

Se vocĂȘ tiver um FnOnce (p.ex. multiply_sum), poderĂĄ chamĂĄ-lo apenas uma vez. Ele 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.

Quando vocĂȘ define uma função que recebe um closure, vocĂȘ deve usar FnOnce se puder (ou seja, vocĂȘ o chama uma vez) ou FnMut caso contrĂĄrio, e por Ășltimo Fn. Isso permite a maior flexibilidade para o chamador.

Em contraste, quando vocĂȘ tem um closure, o mais flexĂ­vel que vocĂȘ pode ter Ă© Fn (ele pode ser passado para qualquer lugar), entĂŁo FnMut e, por Ășltimo, FnOnce.

O compilador também infere Copy (p.ex. para add_3) e Clone (p.ex.

Por padrĂŁo, os closures capturam por referĂȘncia se puderem. A palavra-chave move faz com que eles capturem por valor.

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

fn main() {
    let hi = make_greeter("OlĂĄ".to_string());
    hi("Greg");
}