Óxido: punteros inteligentes

Rust asigna todo en la pila de forma predeterminada. Puede almacenar cosas en el montón envolviéndolas en punteros inteligentes como Box . Tipos como Vec y String ayudan implícitamente a la asignación del montón. Los punteros inteligentes implementan los rasgos enumerados en la siguiente tabla. Estos rasgos de los punteros inteligentes los diferencian de una estructura ordinaria:

No Señor Nombre del rasgo Descripción del paquete
1 Deref

std::ops::Deref

Se utiliza para operaciones de eliminación de referencias inmutables, como * v.

2 soltar

std::ops::Drop

Se usa para ejecutar algún código cuando un valor sale del alcance. Esto a veces se llama destructor.

En este capítulo, aprenderemos sobre la Boxpuntero inteligente. También aprenderemos a crear un puntero inteligente personalizado como Box.

Caja

El puntero inteligente Box, también llamado caja, le permite almacenar datos en el montón en lugar de en la pila. La pila contiene el puntero a los datos del montón. Una caja no tiene una sobrecarga de rendimiento, aparte de almacenar sus datos en el montón.

Veamos cómo usar una caja para almacenar un valor i32 en el montón.

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

Salida

b = 5

Para acceder a un valor señalado por una variable, utilice la eliminación de referencias. El * se utiliza como operador de desreferencia. Veamos cómo usar la desreferencia 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 variable x es un tipo de valor con el valor 5. Entonces, la expresión 5 == x devolverá verdadero. La variable y apunta al montón. Para acceder al valor en el montón, necesitamos desreferenciar usando * y. * y devuelve el valor 5. Entonces, la expresión 5 == * y devuelve verdadero.

Salida

true
true

Ilustración - Deref Trait

El Deref rasgo, proporcionado por la biblioteca estándar, nos exige poner en práctica un método llamado DEREF , que toma prestado uno mismo y devuelve una referencia a los datos interno. El siguiente ejemplo crea una estructura MyBox , que es un tipo genérico. Implementa el rasgo Deref . Este rasgo nos ayuda a acceder a los valores del montón envueltos 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
}

Salida

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

Ilustración - Rasgo de gota

El rasgo Drop contiene el método drop () . Este método se llama cuando una estructura que implementó este rasgo queda fuera de alcance. En algunos lenguajes, el programador debe llamar al código para liberar memoria o recursos cada vez que terminan de usar una instancia de un puntero inteligente. En Rust, puede lograr la desasignación automática de memoria usando el rasgo 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");
}

En el ejemplo anterior, el método drop se llamará dos veces ya que estamos creando dos objetos en el montón.

dropping MyBox object from memory
dropping MyBox object from memory