スマートポインタを活用しよう
from 借用チェッカとの戦いに勝利する
借用チェッカを念頭に置いてデータ構造を設計しよう#679b8dd075d04f000017da1d は「相互に関連するデータ構造を管理するにはスマートポインタを用いよう」というアドバイスの一般的なアプローチである
Rust の一般的な スマートポインタ(項目8:参照型とポインタ型に慣れよう)
Rc<T>
複数の 所有者 を持つことができ、同じアイテムを複数箇所から参照 可能
RefCell<T> と組み合わせることが多い
RefCell<T>(内部可変性)
可変参照 なしで内部の状態を変更できる
しかし、借用チェッカ が コンパイル 時ではなく実行時に走る というコストが発生する
Arc<T>
スレッド間での共有に対応した Rc<T> の スレッドセーフ 版
Mutex<T> / RwLock<T>
スレッドセーフな内部可変性を提供する(RefCell<T> に相当)
複数スレッドでのデータ共有を安全に実現できる(warning.icon 項目17:状態共有並列実行には気を付けよう)
Cell<T>
Copy 型に対して内部可変性を実現する(RefCell<T> の簡易版)
warning.icon
Rc<RefCell<T>> 用いると 借用チェッカ の制約を回避できるが、Rust の安全性の一部を受けられないことがある
e.g. 他に参照が存在する状態で borrow_mut を用いると、コンパイルエラーではなく実行時 panic! が発生 する
子ノードが親ノードへの参照を持つような 木構造 で Rc<RefCell<T>> を用いると、循環参照 が発生する
4. HTMLを解析する──HTMLからDOMツリーへの変換#6756d2f175d04f0000107957
`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 を利用することができる
項目16:unsafeコードを書かないようにしよう
できるだけ crates.io から既存のクレートを探す方が良い
#Rust #Effective_Rust_―_Rustコードを改善し、エコシステムを最大限に活用するための35項目