Ruggine - Puntatori intelligenti
Rust alloca tutto in pila per impostazione predefinita. Puoi memorizzare le cose nell'heap avvolgendole in puntatori intelligenti come Box . Tipi come Vec e String aiutano implicitamente l'allocazione dell'heap. I puntatori intelligenti implementano i tratti elencati nella tabella seguente. Questi tratti dei puntatori intelligenti li differenziano da una struttura ordinaria -
Suor n | Nome del tratto | Pacchetto e descrizione |
---|---|---|
1 | Deref | std::ops::Deref Utilizzato per operazioni di dereferenziazione immutabili, come * v. |
2 | Far cadere | std::ops::Drop Utilizzato per eseguire del codice quando un valore esce dall'ambito. Questo a volte è chiamato un distruttore |
In questo capitolo impareremo a conoscere il Boxpuntatore intelligente. Impareremo anche come creare un puntatore intelligente personalizzato come Box.
Scatola
Il puntatore intelligente Box, chiamato anche box, consente di archiviare i dati sull'heap anziché sullo stack. Lo stack contiene il puntatore ai dati dell'heap. Un Box non ha un sovraccarico delle prestazioni, a parte l'archiviazione dei dati nell'heap.
Vediamo come utilizzare un box per memorizzare un valore i32 sull'heap.
fn main() {
let var_i32 = 5;
//stack
let b = Box::new(var_i32);
//heap
println!("b = {}", b);
}
Produzione
b = 5
Per accedere a un valore puntato da una variabile, utilizzare dereferencing. * Viene utilizzato come operatore di dereferenziazione. Vediamo come utilizzare la dereferenziazione con Box.
fn main() {
let x = 5;
//value type variable
let y = Box::new(x);
//y points to a new value 5 in the heap
println!("{}",5==x);
println!("{}",5==*y);
//dereferencing y
}
La variabile x è un tipo di valore con valore 5. Quindi, l'espressione 5 == x restituirà true. La variabile y punta all'heap. Per accedere al valore in heap, dobbiamo dereferenziare usando * y. * y restituisce il valore 5. Quindi, l'espressione 5 == * y restituisce vero.
Produzione
true
true
Illustrazione - Deref Trait
Il tratto Deref, fornito dalla libreria standard, ci richiede di implementare un metodo chiamato deref , che prende in prestito self e restituisce un riferimento ai dati interni. L'esempio seguente crea una struttura MyBox , che è un tipo generico. Implementa il tratto Deref . Questa caratteristica ci aiuta ad accedere ai valori di heap racchiusi da y usando * y .
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
// Generic structure with static method new
fn new(x:T)-> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0 //returns data
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
// calling static method
println!("5==x is {}",5==x);
println!("5==*y is {}",5==*y);
// dereferencing y
println!("x==*y is {}",x==*y);
//dereferencing y
}
Produzione
5==x is true
5==*y is true
x==*y is true
Illustrazione - Drop Trait
Il tratto Drop contiene il metodo drop () . Questo metodo viene chiamato quando una struttura che ha implementato questo tratto esce dall'ambito. In alcune lingue, il programmatore deve chiamare il codice per liberare memoria o risorse ogni volta che finisce di utilizzare un'istanza di un puntatore intelligente. In Rust, puoi ottenere la deallocazione automatica della memoria usando Drop trait.
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x:T)->MyBox<T>{
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -< &T {
&self.0
}
}
impl<T> Drop for MyBox<T>{
fn drop(&mut self){
println!("dropping MyBox object from memory ");
}
}
fn main() {
let x = 50;
MyBox::new(x);
MyBox::new("Hello");
}
Nell'esempio precedente, il metodo drop verrà chiamato due volte poiché stiamo creando due oggetti nell'heap.
dropping MyBox object from memory
dropping MyBox object from memory