Vai al contenuto
eLearner.app
Modulo 3 · Lezione 2 di 26/14 nel corso~15 min
Lezioni del modulo (2/2)

Riferimenti e Borrowing

In Rust, passare continuamente la proprietà (ownership) di una variabile a una funzione e farsela restituire può essere molto scomodo. Per risolvere questo problema, Rust utilizza i riferimenti (references).

Creare un riferimento a un valore si chiama Borrowing (prendere in prestito).

Riferimenti Immutabili

Un riferimento si dichiara anteponendo il simbolo & al tipo o alla variabile. Per impostazione predefinita, i riferimenti sono immutabili: permettono di leggere il valore ma non di modificarlo.

Code
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

Riferimenti Mutabili

Se hai bisogno di modificare un valore preso in prestito, devi usare un riferimento mutabile tramite &mut. La variabile originale deve anche essere dichiarata mutabile con mut:

Code
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");
}

Le Regole d'Oro del Borrowing

Per prevenire corruzioni di memoria e corse ai dati (data races) a tempo di compilazione, Rust impone due regole fondamentali:

  1. Puoi avere un qualsiasi numero di riferimenti immutabili (&T) a un valore nello stesso momento.
  2. OPPURE puoi avere esattamente un solo riferimento mutabile (&mut T) a un valore alla volta.

Non puoi assolutamente mescolare riferimenti immutabili e mutabili per lo stesso valore nello stesso scope:

Code
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

Lo Scope dei Riferimenti e Non-Lexical Lifetimes (NLL)

In passato, lo scope di un riferimento durava obbligatoriamente fino alla fine del blocco in cui veniva creato. Oggi il compilatore di Rust è più intelligente grazie alle Non-Lexical Lifetimes (NLL): lo scope di un riferimento termina all'ultima riga in cui viene utilizzato, non necessariamente a fine blocco.

Ciò rende valido il seguente codice:

Code
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

Prova tu

Esercizio#rust.m3.l2.e1
Tentativi: 0Caricamento…

Passa un riferimento immutabile di s1 alla funzione calculate_length usando il simbolo &.

Caricamento editor…
Mostra suggerimento

Sostituisci `/* TODO \_/`con`&s1` per passare un riferimento immutabile alla stringa.

Soluzione disponibile dopo 3 tentativi

Esercizio#rust.m3.l2.e2
Tentativi: 0Caricamento…

Rendi la variabile s mutabile (let mut s) e passa un riferimento mutabile (&mut s) alla funzione change per permetterle di modificare la stringa.

Caricamento editor…
Mostra suggerimento

Usa `let mut s`al posto di`let s`e chiama`change(&mut s);`.

Soluzione disponibile dopo 3 tentativi

Esercizio#rust.m3.l2.e3
Tentativi: 0Caricamento…

Dichiara una variabile s contenente String::from("Rust"). Crea due riferimenti immutabili distinti r1 e r2 a s, ed infine stampa i due riferimenti separati da uno spazio usando la macro println!.

Caricamento editor…
Mostra suggerimento

Assegna `let r1 = &s;` e `let r2 = &s;` per ottenere due riferimenti immutabili, poi stampali con `println!("{} {}", r1, r2);`.

Soluzione disponibile dopo 3 tentativi