Cell y RefCell implementan lo que Rust llama mutabilidad interior: mutación de valores en un contexto inmutable.
La celda se usa normalmente para tipos simples, ya que requiere copiar o mover valores. Los tipos de interiores más complejos suelen utilizar RefCell, que rastrea referencias compartidas y exclusivas en tiempo de ejecución y entra en pánico si se utilizan incorrectamente.
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug, Default)]
struct Node {
value: i64,
children: Vec<Rc<RefCell<Node>>>,
}
impl Node {
fn new(value: i64) -> Rc<RefCell<Node>> {
Rc::new(RefCell::new(Node { value, ..Node::default() }))
}
fn sum(&self) -> i64 {
self.value + self.children.iter().map(|c| c.borrow().sum()).sum::<i64>()
}
}
fn main() {
let root = Node::new(1);
root.borrow_mut().children.push(Node::new(5));
let subtree = Node::new(10);
subtree.borrow_mut().children.push(Node::new(11));
subtree.borrow_mut().children.push(Node::new(12));
root.borrow_mut().children.push(subtree);
println!("graph: {root:#?}");
println!("graph sum: {}", root.borrow().sum());
}
Si estuviéramos usando Cell en lugar de RefCell en este ejemplo, tendríamos que sacar el Nodo de Rc para empujar a los elementos secundarios y luego volver a colocarlo. Esto es seguro porque siempre hay un valor sin referencia en la celda, pero no es ergonómico.
Para hacer cualquier cosa con un Nodo, debe llamar a un método RefCell, generalmente borrow o borrow_mut.