El borrowing (prestado) en Rust es un mecanismo de "préstamo" temporal de una variable a través de referencias. Rust distingue entre el préstamo inmutable (&T) y el mutable (&mut T). Puede haber tantas referencias inmutables como sea necesario al mismo tiempo, pero solo una mutable. No se permite la existencia simultánea de referencias mutables e inmutables al mismo objeto.
Esta regla garantiza la ausencia de data race en la etapa de compilación y hace que Rust sea seguro para la programación concurrente.
Ejemplo:
let mut value = 5; let r1 = &value; let r2 = &value; // let r3 = &mut value; // ERROR: no se puede crear &mut mientras hay & println!("{} {}", r1, r2); // r3 está prohibido hasta que termine el ámbito de r1/r2
Pregunta: ¿Se puede crear en un mismo ámbito una referencia &mut y tantas referencias & como se desee a un mismo objeto?
Respuesta incorrecta típica: Sí, pero solo si no se superponen en el tiempo de vida.
Respuesta correcta: No puede existir simultáneamente una referencia mutable y una inmutable a un mismo objeto (incluso si los accesos a ellas no se superponen estáticamente en el código), mientras una esté viva, las demás están prohibidas. La seguridad es verificada por el borrow checker.
Ejemplo:
let mut x = 10; let y = &x; let z = &mut x; // Error, y todavía está en el ámbito println!("{}", y); // y se necesita más tarde
Historia
En un gran proyecto con procesamiento de datos paralelo, un desarrollador decidió usar referencias inmutables a un vector y luego intentó obtener una mutable para ordenar. El código funcionaba en la prueba, pero dejó de compilar después de la refactorización, ya que se amplió el tiempo de vida de las referencias inmutables.
Historia
En un servicio interno, se modificó la estructura a través de &mut, manteniendo al mismo tiempo referencias a los campos para enviarlos a otro hilo más adelante. Surgió una carrera de datos y un crash debido al incumplimiento de las reglas de préstamo: Rust protege contra esto solo en la etapa de compilación, pero los errores ocurrieron en bloques unsafe, donde se levantan las garantías.
Historia
Documentación incorrecta de la API: la biblioteca aceptaba simultáneamente & y &mut en diferentes campos de la estructura, pero debido al aliasing se violaba la invarianza y surgían errores difíciles de rastrear con los servicios que integraban esta biblioteca.