Programación funcional en Rust: nivel avanzado
\n\nRust es un lenguaje multiparadigma que combina de manera orgánica estilos imperativos, orientados a objetos y funcionales. A nivel avanzado, la programación funcional en Rust permite escribir código más seguro, expresivo y conciso. En este artículo, analizaremos conceptos clave: clausuras con captura del entorno, iteradores avanzados, functores y mónadas usando Option y Result, así como currificación y composición de funciones.
1. Clausuras: propiedad, préstamo y mutabilidad
\n\nLas clausuras en Rust no son solo funciones anónimas. Pueden capturar variables del ámbito circundante de tres maneras: por referencia (&T), por referencia mutable (&mut T) y por valor (T). El compilador selecciona automáticamente el trait menos restrictivo: Fn, FnMut o FnOnce.
Veamos un ejemplo con captura por valor y uso de move:
fn main() {\n let data = vec![1, 2, 3];\n \n // Captura por valor (move) — la propiedad se transfiere a la clausura\n let closure = move || {\n println!("{:?}", data);\n // data ya no está disponible en la función principal\n };\n \n closure();\n // println!("{:?}", data); // Error: data ha sido movida\n}\n\n\nTécnica avanzada: uso de clausuras para crear fábricas de funciones:
\n\nfn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {\n move |x| x * factor\n}\n\nfn main() {\n let double = make_multiplier(2);\n let triple = make_multiplier(3);\n \n println!("{}", double(5)); // 10\n println!("{}", triple(5)); // 15\n}\n\n\nAquí, impl Fn(i32) -> i32 es el tipo de retorno de la clausura. Rust genera automáticamente un tipo anónimo que implementa el trait Fn.
2. Iteradores avanzados: cadenas, adaptadores y evaluación perezosa
\n\nLos iteradores en Rust son una herramienta poderosa del estilo funcional. Son perezosos: los cálculos solo ocurren al llamar a un método terminal (collect, for_each, fold, etc.).
Veamos un pipeline complejo de procesamiento de datos:
\n\nfn process_data(data: Vec<i32>) -> Vec<i32> {\n data.iter()\n .filter(|&x| x > 0) // Dejamos los positivos\n .map(|x| x * 2) // Duplicamos\n .enumerate() // Agregamos índices\n .filter(|(i, _)| i % 2 == 0) // Índices pares\n .map(|(_, x)| x) // Extraemos el valor\n .collect() // Método terminal\n}\n\n\nTécnica avanzada: uso de flat_map para trabajar con estructuras anidadas y fold para acumulación:
fn main() {\n let nested = vec![vec![1, 2], vec![3, 4, 5], vec![6]];\n \n let flattened: Vec<i32> = nested.iter()\n .flat_map(|v| v.iter())\n .copied()\n .collect();\n \n println!("{:?}", flattened); // [1, 2, 3, 4, 5, 6]\n \n // fold — análogo funcional de reduce\n let sum = flattened.iter()\n .fold(0, |acc, x| acc + x);\n \n println!("Suma: {}", sum); // 21\n}\n\n\nEs importante recordar: las cadenas de iteradores no crean colecciones intermedias a menos que se llame a collect. Esto ahorra memoria y mejora el rendimiento.
3. Mónadas Option y Result: manejo funcional de errores
\n\nEn programación funcional, Option y Result son mónadas. Permiten encadenar el procesamiento de valores opcionales y errores sin match o if let anidados.
Veamos un escenario típico con verificaciones anidadas:
\n\nBlogs
Book Recommendations
Leer también
Cómo conseguir trabajo como desarrollador Go: consejos y currículum en la práctica
Cómo escribir una API REST en Java en 30 minutos: guía paso a paso
Go contra Python: ¿Existe en Go la construcción "if x in"?
Los mejores IDE y herramientas para desarrollo en Rust en 2024
Java para backend vs frontend: ¿qué elegir en 2024?