Cell e RefCell

Cell e RefCell implementam o que Rust chama de mutabilidade interior: mutação de valores em um contexto imutåvel.

Cell Ă© normalmente usado para tipos simples, pois requer copiar ou mover valores. Tipos interiores mais complexos normalmente usam RefCell, que rastreia referĂȘncias compartilhadas e exclusivas em tempo de execução e retorna um pĂąnico (panic) se forem mal utilizadas.

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());
}
  • Se estivĂ©ssemos usando Cell em vez de RefCell neste exemplo, terĂ­amos que mover o Node para fora do Rc para enviar os filhos e, em seguida, movĂȘ-lo de volta. Isso Ă© seguro porque sempre hĂĄ um, valor nĂŁo referenciado em cell, mas nĂŁo Ă© ergonĂŽmico.
  • Para fazer qualquer coisa com um Node, vocĂȘ deve chamar um mĂ©todo RefCell, geralmente borrow ou borrow_mut.
  • Demonstre que loops de referĂȘncia podem ser criados adicionando root a subtree.children (nĂŁo tente imprimi-lo!).
  • Para demonstrar um pĂąnico em tempo de execução, adicione um fn inc(&mut self) que incrementa self.value e chama o mesmo mĂ©todo em seus filhos. Isso criarĂĄ um pĂąnico na presença do loop de referĂȘncia, com thread 'main' em pĂąnico no 'jĂĄ emprestado: BorrowMutError'.