Я изучаю ржавчину и застрял на вопросе о Rc, Weak и RefCell. Вариант использования — реализовать полностью функционирующее дерево, в котором каждый узел имеет одного родителя и список дочерних элементов. Документация обеспечивает хорошую отправную точку:
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
Но. Когда я пытаюсь расширить это, возникает проблема с дизайном. Я не могу добавить новый узел к уже состоящему. У узла branch есть дочерний узел, но я думаю, что это возможно только потому, что мы уже знаем, что leaf будет единственным дочерним элементом, прежде чем будет создан branch. Если это не так, можем ли мы как-то изменить branch после того, как он уже был создан внутри Rc, и добавить leaf в качестве дочернего?
Или я должен оставить этот дизайн и принять дизайн, который больше похож на:
#[derive(Debug)]
struct Node {
value: i32,
parent: Weak<RefCell<Node>>,
children: Vec<Rc<RefCell<Node>>>,
}

Вы можете добавить новые листья с помощью следующего кода:
let new_leaf = Rc::new(Node {
value: 4,
parent: RefCell::new(Rc::downgrade(&branch)),
children: RefCell::new(vec![])
});
branch.children.borrow_mut().push(new_leaf);
При этом предложенный вами альтернативный тип Node, похоже, является тем, с чем большинство rustaceans пошли бы / с которыми более знакомы.
В том-то и дело, что RefCell он позволяет изменять свое содержимое и требует для этого только общую ссылку, как вы можете видеть в подписи fn borrow_mut(&self) -> RefMut<'_, T>, где RefMut<'_, T> по сути является изменяемой ссылкой &mut T плюс подсчет ссылок во время выполнения.
Очевидно, я недостаточно хорошо понимаю Rc. Получается, вы можете видоизменять объект внутри Rc, пока один из его элементов обернут RefCell? Я знал, что это можно сделать с помощью прямого Rc<RefCell<SomeType>>, но не с помощью Rc<SomeType{ RefCell < SomeMemberType > } >. Спасибо.