¡Bienvenidos de nuevo a Rustaceo.es! En esta entrega exploraremos dos poderosas características de Rust que permiten escribir código más expresivo y eficiente: closures e iterators. Estos conceptos son fundamentales en la programación funcional y ayudan a mejorar la claridad y la seguridad del código.
📝 Introducción a las closures #
Las closures (o funciones anónimas) en Rust son funciones que pueden capturar variables del entorno en el que se definen. Son útiles para operaciones como filtrado, mapeo y manejo de eventos.
⚒ Sintaxis de una closure #
let suma = |x, y| x + y;
let resultado = suma(3, 4);
println!("El resultado es: {}", resultado); // Imprime 7
- 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.
Captura de variables en closures #
Las closures pueden capturar variables de su entorno de tres maneras:
- Por referencia (
&T
) - Por valor (
T
) - Por mutabilidad (
&mut T
)
Ejemplo:
let mut contador = 0;
let mut incrementar = || {
contador += 1;
println!("Contador: {}", contador);
};
incrementar(); // Contador: 1
incrementar(); // Contador: 2
Tipos de closures #
Rust clasifica las closures en tres tipos según cómo capturan valores:
Fn
: Captura por referencia.FnMut
: Captura por referencia mutable.FnOnce
: Captura por valor (se consume después de usarse).
Ejemplo de FnOnce
:
let mensaje = String::from("¡Hola, Rust!");
let consumir = move || println!("{}", mensaje);
consumir();
// mensaje ya no es accesible aquí
📈 Introducción a los iteradores #
Los iteradores en Rust permiten procesar secuencias de datos de manera eficiente sin necesidad de escribir bucles explícitos.
Creación de un iterador #
let numeros = vec![1, 2, 3];
let mut iter = numeros.iter();
println!("{:?}", iter.next()); // Some(1)
println!("{:?}", iter.next()); // Some(2)
println!("{:?}", iter.next()); // Some(3)
println!("{:?}", iter.next()); // None
Métodos de los iteradores #
Los iteradores tienen varios métodos útiles:
map()
: Transforma cada pokémon.filter()
: Filtra elementos según una condición.fold()
: Acumula valores.
Ejemplo de uso:
let numeros = vec![1, 2, 3, 4, 5];
let cuadrados: Vec<i32> = numeros.iter().map(|x| x * x).collect();
println!("{:?}", cuadrados); // [1, 4, 9, 16, 25]
Iteradores vs. bucles #
Usar iteradores puede hacer que el código sea más conciso y seguro.
Ejemplo con un bucle tradicional:
let mut suma = 0;
for num in &numeros {
suma += num;
}
println!("Suma: {}", suma);
Usando iter().sum()
:
let suma: i32 = numeros.iter().sum();
println!("Suma: {}", suma);
Iteradores personalizados #
También podemos crear nuestros propios iteradores implementando el trait Iterator
.
Ejemplo:
struct Contador {
actual: u32,
max: u32,
}
impl Iterator for Contador {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.actual < self.max {
self.actual += 1;
Some(self.actual)
} else {
None
}
}
}
let mut contador = Contador { actual: 0, max: 5 };
while let Some(num) = contador.next() {
println!("{}", num);
}
🎯 Combinando closures e iteradores #
Las closures y los iteradores se complementan muy bien en Rust para manejar colecciones de manera eficiente.
Ejemplo:
let numeros = vec![1, 2, 3, 4, 5];
let filtrados: Vec<_> = numeros.into_iter().filter(|x| x % 2 == 0).collect();
println!("{:?}", filtrados); // [2, 4]
✅ Conclusión #
Rust proporciona herramientas poderosas para escribir código funcional con closures e iteradores. Al usarlos correctamente, podemos hacer nuestro código más legible, seguro y eficiente.
🔮 Próximo paso: ¡Intenta implementar tus propias funciones utilizando closures e iteradores en tus proyectos de Rust!
¡Hasta la próxima, Rustaceos!