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)); }
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"); }