Las closures en Rust son funciones anónimas que tienen la capacidad única de “capturar” variables de su entorno. A diferencia de las funciones estándar (fn), las closures pueden llevar consigo el estado de las variables locales del ámbito donde fueron definidas. Rust utiliza un sistema de inferencia de tipos extremadamente inteligente para determinar la forma más eficiente de realizar esta captura, clasificándolas internamente según cómo interactúan con el entorno.
Existen tres modos de captura:
1. Por referencia inmutable (&T): La closure solo lee el valor. Esto ocurre cuando el cuerpo de la closure no modifica la variable y la devuelve de forma que no consume su propiedad.
2. Por referencia mutable (&mut T): La closure puede modificar el valor capturado.
3. Por valor (T): La closure toma la propiedad (ownership) de la variable, consumiéndola.
Si intentas capturar una variable por referencia en un contexto donde la variable podría ser destruida antes que la closure (como al lanzar un hilo con std::thread::spawn), el compilador de Rust arrojará un error de lifetimes. Asimismo, si una closure captura una variable por valor mediante la palabra clave move, esa variable queda invalidada en el scope original, lo que puede causar errores de “use of moved value” si intentas acceder a ella después.
use std::thread;
fn main() {
let text = String::from("Hola");
let mut count = 0;
// 1. Captura por referencia inmutable (&String)
// El compilador infiere que print_text solo necesita leer 'text'.
let print_text = || println!("Texto capturado: {}", text);
print_text();
// 2. Captura por referencia mutable (&mut i32)
// Al modificar 'count', el compilador infiere captura por referencia mutable.
let mut increment = || count += 1;
increment();
increment();
println!("Valor de count tras increment: {}", count);
// 3. Captura por valor mediante 'move' (Uso en Threads)
// 'text_cloned' es una copia para no invalidar 'text' prematuramente.
let text_cloned = text.clone();
let handle = thread::spawn(move || {
// El 'move' transfiere la propiedad de 'text_cloned' al nuevo hilo.
println!("Mensaje desde el hilo (con move): {}", text_cloned);
});
handle.join().unwrap();
// 4. Captura por valor (Ownership transfer)
// Usamos 'move' para forzar que 'text' sea propiedad de la closure.
let closure_final = move || {
println!("Closure final con ownership de text: {}", text);
};
closure_final();
// println!("{}", text); // ERROR: 'text' fue movido a 'closure_final'
}
Explicación del Código
text(String): Es una variable del entorno. En la primera closureprint_text, se captura por referencia inmutable. Sin embargo, al final del programa, es movida por la closureclosure_final.count(i32): Variable mutuable. La closureincrementla captura por referencia mutable (&mut i32) porque la modifica internamente, permitiendo que el valor cambie en el scope demain.print_text: Una closure que implementa el traitFn, ya que solo realiza lecturas detext.increment: Una closure que implementa el traitFnMut, debido a que necesita modificar el valor decount.text_cloned: Una copia detextcreada para permitir que el hilo sea el dueño de sus propios datos.thread::spawn(move || ...): El uso demoveaquí es obligatorio. Sinmove, el hilo intentaría capturartext_clonedpor referencia, pero como el hilo puede vivir más que la funciónmain, Rust prohibiría esto por seguridad de memoria.closure_final: Al usarmove, la propiedad detextse transfiere permanentemente al cuerpo de la closure. Por esta razón, cualquier intento de usartextdespués de llamar aclosure_final()resultaría en un error de compilación.
N° 62