Cell
y RefCell
Cell
y RefCell
implementan lo que Rust llama mutabilidad interna: mutación de valores en un contexto inmutable.
Normalmente, Cell
se utiliza para tipos simples, ya que requiere copiar o mover valores. Los tipos internos más complejos normalmente utilizan RefCell
, que realiza un seguimiento de las referencias compartidas y exclusivas en tiempo de ejecución y entra en pánico si se utilizan de forma incorrecta.
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 utilizando
Cell
en lugar deRefCell
en este ejemplo, tendríamos que mover elNode
fuera delRc
para insertar hijos y luego volver a moverlo. Esto es seguro porque siempre hay un valor sin referenciar en la celda, pero no es ergonómico. - Para hacer cualquier cosa con un Node, debes llamar a un método de
RefCell
, normalmenteborrow
oborrow_mut
. - Demuestra que se pueden crear bucles de referencia añadiendo
root
asubtree.children
(¡no intentes imprimirlo!). - Para demostrar un pánico en tiempo de ejecución, añade un
fn inc(&mut self)
que incrementeself.value
y llame al mismo método en sus hijos. Esto entrará en pánico en presencia del bucle de referencia, conthread 'main' panicked at 'already borrowed: BorrowMutError'
.