Lecciones del módulo (2/2)
Referencias y Borrowing
En Rust, pasar continuamente la propiedad (ownership) de una variable a una función y hacer que la devuelva puede ser muy incómodo. Para resolver este problema, Rust utiliza los referencias (references).
Crear una referencia a un valor se llama Borrowing (tomar prestado).
Referencias Inmutables
Una referencia se declara anteponiendo el símbolo & al tipo o a la variable. Por defecto, las referencias son inmutables: permiten leer el valor pero no modificarlo.
fn main() {
let s1 = String::from("hello");
// Passiamo un riferimento a s1, non l'ownership
let len = calculate_length(&s1);
// s1 è ancora utilizzabile qui!
println!("La lunghezza di '{}' è {}.", s1, len);
}
fn calculate_length(s: &String) -> usize { // s è un riferimento a una String
s.len()
} // Qui s esce dallo scope, ma poichè non possiede il valore, non succede nulla
Referencias Mutables
Si necesitas modificar un valor tomado prestado, debes usar una referencia mutable mediante &mut. La variable original también debe declararse como mutable con mut:
fn main() {
let mut s = String::from("hello");
// Passiamo un riferimento mutabile
change(&mut s);
println!("{}", s); // Stampa "hello, world"
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
Las Reglas de Oro del Borrowing
Para prevenir corrupciones de memoria y condiciones de carrera (data races) en tiempo de compilación, Rust impone dos reglas fundamentales:
- Puedes tener cualquier número de referencias inmutables (
&T) a un valor al mismo tiempo. - O BIEN puedes tener exactamente una sola referencia mutable (
&mut T) a un valor a la vez.
No puedes mezclar bajo ningún concepto referencias inmutables y mutables para el mismo valor en el mismo ámbito:
let mut s = String::from("hello");
let r1 = &s; // Valido
let r2 = &s; // Valido
// let r3 = &mut s; // ERRORE DI COMPILAZIONE! Non puoi creare &mut s se s è già presa in prestito come immutabile
El Ámbito de las Referencias y Non-Lexical Lifetimes (NLL)
En el pasado, el ámbito de una referencia duraba obligatoriamente hasta el final del bloque en el que se creaba. Hoy en día, el compilador de Rust es más inteligente gracias a las Non-Lexical Lifetimes (NLL): el ámbito de una referencia termina en la última línea en la que se utiliza, no necesariamente al final del bloque.
Esto hace que el siguiente código sea válido:
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} e {}", r1, r2); // Ultimo uso di r1 e r2. I riferimenti immutabili scadono qui!
let r3 = &mut s; // Valido! Nessun riferimento immutabile è attivo a questo punto
Pruébalo tú
Pasa una referencia inmutable de s1 a la función calculate_length usando el símbolo &.
Mostrar pista
Reemplaza `/* TODO \_/`con`&s1` para pasar una referencia inmutable a la cadena.
Solución disponible después de 3 intentos
Haz que la variable s sea mutable (let mut s) y pasa una referencia mutable (&mut s) a la función change para permitirle modificar la cadena.
Mostrar pista
Usa `let mut s`en lugar de`let s`y llama a`change(&mut s);`.
Solución disponible después de 3 intentos
Declara una variable s que contenga String::from("Rust"). Crea dos referencias inmutables distintas r1 y r2 a s, y finalmente imprime las dos referencias separadas por un espacio usando la macro println!.
Mostrar pista
Asigna `let r1 = &s;` y `let r2 = &s;` para obtener dos referencias inmutables, luego imprímelas con `println!("{} {}", r1, r2);`.
Solución disponible después de 3 intentos