En Rust, cuando quieres acceder al valor de una variable sin tomar posesión de ella (es decir, sin mover el ownership), utilizas una referencia inmutable mediante la sintaxis &T. Una referencia es esencialmente un puntero que apunta a una ubicación en memoria donde reside un valor, pero con la garantía de que ese valor no será modificado ni destruido mientras la referencia esté activa.

El uso de referencias inmutables es fundamental para la eficiencia. En lugar de copiar grandes estructuras de datos, simplemente pasas una dirección de memoria. Rust permite tener múltiples referencias inmutables (&T) al mismo tiempo sobre el mismo dato, lo que facilita la lectura concurrente. Sin embargo, existe una regla de oro: si tienes una referencia inmutable activa, no puedes crear una referencia mutable (&mut T) al mismo tiempo para ese mismo valor. Esto evita condiciones de carrera y errores de memoria.

Si intentas usar una referencia que apunta a un valor que ya ha sido liberado (un “puntero colgante”), el compilador de Rust intervendrá. Gracias al Borrow Checker, Rust garantiza que el ciclo de vida (lifetime) de la referencia nunca exceda al ciclo de vida del valor original. Si intentas realizar una operación que viole la propiedad de los datos o la seguridad de la memoria, el programa simplemente no compilará.

// Función que recibe una referencia inmutable a un String
fn mostrar_texto(texto: &String) {
    // El parámetro 'texto' es un préstamo (borrow) de la variable original
    println!("El contenido es: {}", texto);
}

fn main() {
    // 'saludo' es el dueño (owner) del dato en memoria
    let saludo = String::from("Hola Rust");

    // Creamos múltiples referencias inmutables al mismo valor
    let ref1 = &saludo;
    let ref2 = &saludo;
    let ref3 = &saludo;

    // Podemos usar las referencias para llamar a métodos
    // Rust aplica "auto-deref" para acceder al método len()
    let longitud = &ref1.len(); 

    println!("Longitud detectada: {}", longitud);

    // Pasamos las referencias a la función
    mostrar_texto(ref1);
    mostrar_texto(ref2);
    mostrar_texto(ref3);

    // El valor 'saludo' sigue siendo válido aquí porque no hubo movimiento (move)
    println!("Valor original intacto: {}", saludo);
}

Explicación del Código

  • let saludo = String::from("Hola Rust");: Se crea una variable de tipo String. saludo es el dueño único de este dato en el heap.
  • let ref1 = &saludo;, ref2, ref3: Mediante el operador &, creamos tres referencias inmutables de tipo &String. Cada una apunta a la misma dirección de memoria de saludo.
  • &ref1.len(): Aquí ocurre una combinación de operaciones. Se llama al método .len() sobre ref1. Rust realiza un auto-deref para entender que queremos el método del String y no del puntero. Al poner & delante, estamos obteniendo una referencia a la longitud.
  • fn mostrar_texto(texto: &String): La función define que no quiere ser dueña de la cadena, sino que solo pide un préstamo temporal (&String). Esto permite que la función sea llamada múltiples veces sin perder el control de saludo en main.
  • mostrar_texto(ref1);: Se pasa la referencia ref1 a la función. El parámetro texto dentro de la función ahora apunta al mismo contenido que saludo.
  • println!("{}", saludo);: Esta línea es válida porque las referencias ref1, ref2 y ref3 ya no se están usando (su alcance terminó tras las llamadas a la función), permitiendo que el dueño original recupere el control total para la impresión final.

27