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

Le regole di Ownership

L'Ownership (proprietà) è la caratteristica più distintiva di Rust. Consente al linguaggio di garantire la sicurezza della memoria senza il bisogno di un Garbage Collector o di costringerti a deallocare manualmente la memoria heap (come in C o C++).

Le Tre Regole dell'Ownership

La gestione della memoria in Rust è governata da tre semplici regole controllate rigorosamente dal compilatore:

  1. Ogni valore in Rust ha una variabile chiamata il suo proprietario (owner).
  2. Può esserci solo un proprietario alla volta.
  3. Quando il proprietario esce dallo scope (portata), il valore viene eliminato automaticamente.

Stack vs Heap

Per comprendere l'ownership, è importante sapere dove risiedono i dati:

  • Stack: memorizza dati a dimensione fissa e nota al momento della compilazione (es. interi, booleani). L'accesso è estremamente veloce.
  • Heap: memorizza dati a dimensione dinamica e sconosciuta al momento della compilazione (es. String). L'allocazione è più lenta e richiede un puntatore memorizzato sullo Stack.

Il concetto di "Move" (Spostamento)

Quando assegniamo una variabile memorizzata nello Heap a un'altra, Rust sposta (o trasferisce) la proprietà del valore, invalidando la prima variabile:

Code
let s1 = String::from("ciao");
let s2 = s1; // La proprieta del testo si sposta a s2. s1 NON e piu utilizzabile!

// println!("{}", s1); // ERRORE DI COMPILAZIONE! s1 e "borrow of moved value"
println!("{}", s2); // Valido!

Questo comportamento evita il problema del "double free error" (cercare di liberare la stessa memoria due volte all'uscita dello scope), poiché solo s2 libererà la memoria.

Il concetto di "Clone" (Copia Profonda)

Se abbiamo l'esplicita necessità di copiare l'intero contenuto di una String (sia i puntatori sullo stack che i dati testuali effettivi nello heap), possiamo ricorrere al metodo .clone(). Questo duplica completamente i dati sul heap, mantenendo valide sia la variabile originale che quella nuova, a patto di accettare un costo prestazionale dovuto alla nuova allocazione di memoria:

Code
let s1 = String::from("ciao");
let s2 = s1.clone(); // Copia profonda. Entrambe le variabili rimangono valide!
println!("s1: {}, s2: {}", s1, s2);

Il concetto di "Copy"

Per i tipi semplici salvati interamente sullo Stack (come interi i32, booleani bool, caratteri char), l'assegnazione effettua una vera e propria copia superficiale automatica, perciò entrambe le variabili rimangono valide:

Code
let x = 5;
let y = x; // Copia il valore 5 nello stack. Entrambe le variabili sono utilizzabili!
println!("x: {}, y: {}", x, y); // Valido!

Prova tu

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

Dichiara una String s1 contenente il testo 'hello'. Assegna s1 a s2 (in modo da spostare la proprietà). Infine stampa s2 a schermo usando println!.

Caricamento editor…
Mostra suggerimento

Usa `let s2 = s1;` per trasferire la proprietà a `s2` e stampala con `println!('{}', s2);`.

Soluzione disponibile dopo 3 tentativi

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

Dichiara una variabile intera x con valore 5. Assegna x a y (effettuando una copia). Infine stampa sia x che y nella stessa istruzione println!.

Caricamento editor…
Mostra suggerimento

Fai `let y = x;` e poi stampa entrambi i valori, es. `println!('{} {}', x, y);`.

Soluzione disponibile dopo 3 tentativi

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

Dichiara una String s1 con valore "Rust". Crea una copia profonda di s1 in una nuova variabile s2 usando il metodo clone. Infine, stampa sia s1 che s2 separati da uno spazio usando la macro println!.

Caricamento editor…
Mostra suggerimento

Usa `let s2 = s1.clone();` per copiare in profondità il valore di `s1` in `s2`, quindi stampali con `println!("{} {}", s1, s2);`.

Soluzione disponibile dopo 3 tentativi