Ferrugem - Smart Pointers

Rust aloca tudo na pilha por padrão. Você pode armazenar coisas na pilha envolvendo-as em ponteiros inteligentes como Box . Tipos como Vec e String ajudam implicitamente na alocação de heap. Os ponteiros inteligentes implementam as características listadas na tabela abaixo. Essas características dos ponteiros inteligentes os diferenciam de uma estrutura comum -

Sr. Não Nome do traço Pacote e descrição
1 Deref

std::ops::Deref

Usado para operações de desreferenciamento imutáveis, como * v.

2 Solta

std::ops::Drop

Usado para executar algum código quando um valor sai do escopo. Isso às vezes é chamado de destruidor

Neste capítulo, aprenderemos sobre o Boxponteiro inteligente. Também aprenderemos como criar um ponteiro inteligente personalizado como o Box.

Caixa

O ponteiro inteligente Box, também chamado de caixa, permite armazenar dados na pilha em vez de na pilha. A pilha contém o ponteiro para os dados do heap. Um Box não tem sobrecarga de desempenho, exceto armazenar seus dados no heap.

Vamos ver como usar uma caixa para armazenar um valor i32 no heap.

fn main() {
   let var_i32 = 5; 
   //stack
   let b = Box::new(var_i32); 
   //heap
   println!("b = {}", b);
}

Resultado

b = 5

Para acessar um valor apontado por uma variável, use dereferencing. O * é usado como um operador de desreferência. Vamos ver como usar a desreferência com o 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
}

A variável x é um tipo de valor com o valor 5. Portanto, a expressão 5 == x retornará verdadeiro. A variável y aponta para a pilha. Para acessar o valor no heap, precisamos cancelar a referência usando * y. * y retorna o valor 5. Portanto, a expressão 5 == * y retorna verdadeiro.

Resultado

true
true

Ilustração - Deref Trait

O traço Deref, fornecido pela biblioteca padrão, requer que implementemos um método denominado deref , que toma emprestado a si mesmo e retorna uma referência aos dados internos. O exemplo a seguir cria uma estrutura MyBox , que é um tipo genérico. Ele implementa o traço Deref . Essa característica nos ajuda a acessar os valores de heap envolvidos por 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
}

Resultado

5==x is true
5==*y is true
x==*y is true

Ilustração - Traço de queda

O traço Drop contém o método drop () . Este método é chamado quando uma estrutura que implementou essa característica sai do escopo. Em algumas linguagens, o programador deve chamar o código para liberar memória ou recursos sempre que terminar de usar uma instância de um ponteiro inteligente. No Rust, você pode obter desalocação automática de memória usando o traço Drop.

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

No exemplo acima, o método drop será chamado duas vezes, pois estamos criando dois objetos no heap.

dropping MyBox object from memory
dropping MyBox object from memory