Scope delle Variabili in Rust
In Rust, lo scope di una variabile determina dove è accessibile e quando viene deallocata. Il sistema di ownership si basa fondamentalmente sugli scope.
Scope di Blocco
Ogni coppia di {} crea un nuovo scope. Le variabili vengono rilasciate (drop) alla fine del blocco:
fn main() {
let x = 5;
{
let y = 10;
println!("{} {}", x, y); // OK: entrambe visibili
} // y viene rilasciata qui
println!("{}", x); // OK
// println!("{}", y); // ERRORE: y non esiste più
}
Ownership e Scope
Quando una variabile esce dallo scope, Rust chiama automaticamente drop:
fn main() {
{
let s = String::from("ciao"); // s entra nello scope
println!("{}", s);
} // s esce dallo scope e la memoria viene liberata
// s non è più accessibile qui
}
Move e Scope
Con il move semantics, l’ownership viene trasferita:
fn main() {
let s1 = String::from("ciao");
let s2 = s1; // s1 viene "moved" in s2
// println!("{}", s1); // ERRORE: s1 non è più valida
println!("{}", s2); // OK
}
Scope nelle Funzioni
I parametri entrano nello scope della funzione e ne escono alla fine:
fn prendi_ownership(s: String) {
println!("{}", s);
} // s viene rilasciata qui
fn solo_riferimento(s: &String) {
println!("{}", s);
} // s (il riferimento) esce dallo scope, ma il dato originale no
fn main() {
let s = String::from("ciao");
solo_riferimento(&s); // Prestito: s resta valida
prendi_ownership(s); // Move: s non è più valida
// println!("{}", s); // ERRORE
}
Scope nei Condizionali e Cicli
fn main() {
let condizione = true;
if condizione {
let temporanea = String::from("esiste solo qui");
println!("{}", temporanea);
} // temporanea rilasciata
for i in 0..3 {
let elemento = format!("item_{}", i);
println!("{}", elemento);
} // elemento rilasciata ad ogni iterazione
}
Il Trait Drop
Puoi personalizzare il comportamento alla fine dello scope:
struct Risorsa {
nome: String,
}
impl Drop for Risorsa {
fn drop(&mut self) {
println!("Rilascio risorsa: {}", self.nome);
}
}
fn main() {
let r1 = Risorsa { nome: "file.txt".into() };
let r2 = Risorsa { nome: "connessione".into() };
println!("Risorse create");
} // Stampa: Rilascio risorsa: connessione, poi: Rilascio risorsa: file.txt
// L'ordine di drop è inverso rispetto alla dichiarazione (LIFO)
Drop Anticipato
fn main() {
let r = Risorsa { nome: "lock".into() };
// ... usa la risorsa ...
drop(r); // Rilascia esplicitamente prima della fine dello scope
// ... altro codice che non ha bisogno della risorsa ...
}
Conclusione
Lo scope in Rust è strettamente legato al sistema di ownership. Le variabili vengono automaticamente deallocate alla fine del loro scope, eliminando la necessità di gestione manuale della memoria. Questo meccanismo RAII (Resource Acquisition Is Initialization) garantisce che le risorse vengano sempre rilasciate correttamente.