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