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