Adrianistán

El blog de Adrián Arroyo


Box, Rc y RefCell, punteros inteligentes en Rust

- Adrián Arroyo Calle

Hasta ahora hemos trabajado con tipos primitivos, con estructuras o con tipos de la librería estándar como String o Vec. Sin embargo, ¿si queremos implementar algo similar a String como lo haríamos? Aquí entran en juego, los punteros inteligentes, punteros con capacidades extra. Los más importantes son Box, Rc y RefCell.

Box, reservar memoria en el heap


Box es parecido a malloc de C. Reservan memoria en la parte alta de la memoria. El uso principal de Box en Rust es el de implementar estructuras de datos cíclicas o recursivas.


fn main() {
let n = Box::new(42);
println!("n = {}", n);
}


No es muy útil usarlo así. Solo compensa usarlo en situaciones donde es necesario que la variable ocupe un tamaño fijo, que a priori es indeterminado.

Rc, ¡viva la multipropiedad!


Rc es un puntero inteligente algo más interesante. Permite que un dato sea propiedad de varias variables a la vez. Funciona con un mecanismo de recolector de basura. La clave está en que cuando hagamos clone de un Rc no obtendremos una copia exacta, sino una referencia más al dato original. Esto permite ahorrar memoria. Veamos en un ejemplo, como Regex se comparte entre varias variables. Se trata del mismo Regex, en ningún momento ocurre una duplicidad en memoria.


extern crate regex;

use regex::Regex;
use std::rc::Rc;


fn main() {
let r = Rc::new(Regex::new(r"([A-Za-z0-9])([.]|[_]|[A-Za-z0-9])+@gmail.com").unwrap());
if r.is_match("pucela_segunda@gmail.com") {
println!("Correo válido de Gmail");
}else{
println!("Correo no válido de Gmail");
}

let puntero = r.clone();
println!("Reference Count: {}",Rc::strong_count(&puntero));
puntero.is_match("perro@gmail.com");
r.is_match("_pepe@gmail.com");
}


La línea Reference Count nos dice cuantas variables tienen ahora mismo acceso a ese dato. Rc funciona a la perfección en un solo hilo, pero si quieres hacer lo mismo entre hilos debes usar Arc. Rc por sí mismo, solo permite lecturas, es cuando lo juntamos con RefCell cuando obtenemos soporte de escritura.

RefCell, saltándonos las normas


En primer lugar, RefCell es muy interesante y poderoso, pero no es recomendable usarlo si se pueden usar otros métodos. Digamos que RefCell permite llevar las normas del compilador de Rust sobre préstamos y dueños al runtime. Esto puede provocar crasheos así que normalmente se usa con Rc que previene estas situaciones.


use std::rc::Rc;
use std::cell::RefCell;

fn main(){
let n = Rc::new(RefCell::new(42));
let x = n.clone();
let y = n.clone();
*x.borrow_mut() += 10;
*y.borrow_mut() += 10;
println!("N: {:?}",n.borrow());
}


El resultado de este código es 62. Por tanto, hemos conseguido que distintas variables pudiesen mutar el dato.

Con esto ya hemos visto los punteros inteligentes más importantes de Rust. Existe alguno más como Cell que sin embargo, no es tan usado.

 

Comentarios

Añadir comentario

Todos los comentarios están sujetos a moderación