スマートポインタを活用しよう
Rc<T>
複数の 所有者 を持つことができ、同じアイテムを複数箇所から参照 可能 RefCell<T> と組み合わせることが多い
Arc<T>
Mutex<T> / RwLock<T>
スレッドセーフな内部可変性を提供する(RefCell<T> に相当)
Cell<T>
Copy 型に対して内部可変性を実現する(RefCell<T> の簡易版)
warning.icon
Rc<RefCell<T>> 用いると 借用チェッカ の制約を回避できるが、Rust の安全性の一部を受けられないことがある e.g. 他に参照が存在する状態で borrow_mut を用いると、コンパイルエラーではなく実行時 panic! が発生 する
子ノードが親ノードへの参照を持つような 木構造 で Rc<RefCell<T>> を用いると、循環参照 が発生する `https://effective-rust.com/images/treedatastructure.svg
回避策: Rc<T>(強い参照) よりも 弱い参照 である Weak<T> を使う code:rs
use std::{
cell::RefCell,
rc::{Rc, Weak},
}
struct TreeId(String);
struct BranchId(String);
struct LeafId(String);
struct Tree {
id: TreeId,
branches: Vec<Rc<RefCell<Branch>>>,
}
struct Branch {
id: BranchId,
leaves: Vec<Rc<RefCell<Leaf>>>,
owner: Option<Weak<RefCell<Tree>>>,
}
struct Leaf {
id: LeafId,
owner: Option<Weak<RefCell<Branch>>>,
}
Weak 参照は、メインの参照をインクリメントしないので、アイテムが ドロップ されていないことを明示的にチェックする必要がある code:rs
impl Branch {
fn add_leaf(branch: Rc<RefCell<Branch>>, mut leaf: Leaf) {
leaf.owner = Some(Rc::downgrade(&branch));
branch.borrow_mut().leaves.push(Rc::new(RefCell::new(leaf)));
}
fn location(&self) -> String {
match &self.owner {
None => format!("<unowned>.{}", self.id.0),
Some(owner) => {
let tree = owner.upgrade().expect("owner gone!");
format!("{}.{}", tree.borrow().id.0, self.id.0);
}
}
}
スマートポインタでも表現できないならば、最後の手段として 生ポインタ を扱う unsafe を利用することができる