Ó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