Cuando se pasa una variable a una función, puede ser pasada por referencia (borrow, usando & o &mut), o puede ser movida (move, sin referencia).
Borrow: se pasa una referencia a los datos. Los datos siguen estando disponibles después de la llamada a la función, pero no se puede cambiar el contenido para una referencia inmutable, y solo puede haber 1 referencia mutable activa.
fn read_length(s: &String) -> usize { s.len() }
Move: la variable se "traslada" completamente a la función. Después de pasarla, no podrás usar la variable original — ha sido movida, y cualquier intento de acceder a ella resultará en un error de compilación.
fn destroy(s: String) { println!("{}", s); } // s será destruida al salir let s = String::from("world"); destroy(s); // s ya no se puede usar
Esto evita el problema de liberar memoria dos veces y otros errores de propiedad.
¿Puedo usar la variable después de haberla pasado a una función por valor (move)?
¡No! Después de pasar la variable por valor — por ejemplo, String — la variable original se vuelve inválida:
let s = String::from("abc"); consume(s); // s ya no es válida aquí println!("{}", s); // error de compilación
Muchos confunden esto con el comportamiento de tipos que implementan Copy (por ejemplo, i32), donde la variable sigue siendo válida después de pasarla.
Historia
Un joven programador escribió una función para procesar cadenas que aceptaba String en lugar de &String. Como resultado, la cadena original se volvía inaccesible después de la llamada a la función, lo que llevó a una doble carga y a una asignación adicional de memoria en el servidor de registro.
Historia
En un microservicio, un recurso crítico se pasaba a través de diferentes hilos usando move, y los intentos de acceder a ese recurso en el hilo principal resultaban en un error de compilación. Fue necesario refactorizar urgentemente la arquitectura para que el recurso se pasara por referencia o a través de envoltorios como Arc.
Historia
En un parser interno de eventos, al procesar datos, la variable se tomaba rápidamente (move) en una closure, después de lo cual los accesos a ella fuera del closure generaban errores de compilación. El problema solo se notó durante la revisión: se introdujo un estilo de uso obligatorio de borrow para los datos que debían vivir más allá de la función local.