Translate

lunes, 4 de diciembre de 2023

Closures en Rust


Los Closures o expresiones lambda tienen tipos que no se pueden nombrar. Sin embargo, implementan características especiales de Fn, FnMut y 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));

}


Una Fn (por ejemplo, add_3) no consume ni muta los valores capturados, o tal vez no captura nada en absoluto. Se puede llamar varias veces al mismo tiempo.

Un FnMut (por ejemplo, acumular) podría mutar los valores capturados. Puedes llamarlo varias veces, pero no al mismo tiempo.

Si tiene un FnOnce (por ejemplo, multiplicar_sum), solo puede llamarlo una vez. Podría consumir valores capturados.

FnMut es un subtipo de FnOnce. Fn es un subtipo de FnMut y FnOnce. Es decir. puede usar un FnMut donde sea que se requiera un FnOnce, y puede usar un Fn donde sea que se requiera un FnMut o FnOnce.

El compilador también infiere Copy (por ejemplo, para add_3) y Clone (por ejemplo, multiply_sum), dependiendo de lo que capture el Closure.

De forma predeterminada, los Closures trabajan por referencia si pueden. La palabra clave move los hace capturar por valor.


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

}