27 - Explorando closures - funciones anónimas en Rust

27 - Explorando closures - funciones anónimas en Rust

¡Bienvenidos de nuevo a Rustaceo.es! En nuestra serie sobre Rust y Pokémon, hoy profundizaremos en closures, una característica poderosa en Rust que nos permite definir funciones anónimas flexibles y eficientes. Exploraremos cómo funcionan, cómo capturan valores y cómo pueden mejorar nuestro código mediante ejemplos del mundo Pokémon. ¡Acompáñame en esta aventura!


🔧 ¿Qué son las closures? #

Las closures en Rust son funciones anónimas que pueden capturar variables del entorno en el que se definen. Son especialmente útiles para operaciones funcionales como filtrado, mapeo y procesamiento de datos.

⚒ Sintaxis de una closure #

let suma = |x, y| x + y;
println!("El resultado es: {}", suma(5, 3)); // Imprime 8
  • Se usa | para definir los parámetros.
  • No es necesario especificar los tipos si pueden inferirse.
  • El cuerpo puede ser una única expresión o un bloque de código.

Closures con bloques #

Podemos definir closures con más lógica dentro de un bloque:

let multiplicar = |x, y| {
    let resultado = x * y;
    println!("Multiplicando {} * {}", x, y);
    resultado
};

println!("Resultado: {}", multiplicar(4, 6));

📈 Captura de variables en closures #

Las closures pueden capturar variables del entorno de tres maneras:

  1. Por referencia (&T)
  2. Por mutabilidad (&mut T)
  3. Por valor (T)

Ejemplo:

let mut contador = 0;
let mut incrementar = || {
    contador += 1;
    println!("Contador: {}", contador);
};

incrementar(); // Contador: 1
incrementar(); // Contador: 2

En este caso, la closure captura contador por mutabilidad (&mut T).

Captura por valor #

Si queremos que una closure tome posesión de una variable, podemos usar move:

let mensaje = String::from("¡Hola, Rust!");
let imprimir = move || println!("{}", mensaje);
imprimir();
// `mensaje` ya no es accesible aquí

📁 Closures en iteradores y funciones funcionales #

Las closures son ideales para trabajar con iteradores y programación funcional.

Uso con map() #

let elemento_niveles = vec![5, 12, 22, 30];
let duplicados: Vec<_> = elemento_niveles.iter().map(|x| x * 2).collect();
println!("Niveles duplicados: {:?}", duplicados); // [10, 24, 44, 60]

Uso con filter() #

let niveles_altos: Vec<_> = elemento_niveles.into_iter().filter(|&x| x >= 20).collect();
println!("Pokémon con nivel alto: {:?}", niveles_altos); // [22, 30]

🎯 Tipos de closures en Rust #

Rust clasifica las closures en tres tipos según cómo capturan valores:

  • Fn: Captura por referencia.
  • FnMut: Captura por mutabilidad.
  • FnOnce: Captura por valor y se consume tras su ejecución.

Ejemplo de FnOnce:

fn ejecutar<F: FnOnce()>(func: F) {
    func();
}

let mensaje = String::from("Ejecutando una closure");
ejecutar(move || println!("{}", mensaje));

Aquí, la closure se consume al ejecutarse porque toma la propiedad de mensaje.


📊 Conclusión #

Las closures son una herramienta poderosa en Rust para escribir código más expresivo y modular. Combinadas con iteradores y programación funcional, hacen que el código sea más limpio y seguro.

🔮 Próximo paso: ¡Experimenta con closures en tus proyectos y optimiza tu código!

¡Hasta la próxima, Rustaceos!