読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(rust report) RcとDeref

Interior mutability in Rust: what, why, how?

より, Rustでグラフ構造を作る場合は,

  • RefCellを使って書換え可能にする
  • Rcを使って参照を1つ以上受けれるようにする

ことが要点.

type NodeRef<T> = Rc<RefCell<_Node<T>>>;

// The private representation of a node.
struct _Node<T> {
    inner_value: T,
    adjacent: Vec<NodeRef<T>>,
}

// The public representation of a node, with some syntactic sugar.
struct Node<T>(NodeRef<T>);

impl<T> Node<T> {
    // Creates a new node with no edges.
    fn new(inner: T) -> Node<T> {
        let node = _Node { inner_value: inner, adjacent: vec![] };
        Node(Rc::new(RefCell::new(node)))
    }

    // Adds a directed edge from this node to other node.
    fn add_adjacent(&self, other: &Node<T>) {
        (self.0.borrow_mut()).adjacent.push(other.0.clone());
    }
}

このうち

    fn add_adjacent(&self, other: &Node<T>) {
        (self.0.borrow_mut()).adjacent.push(other.0.clone());
    }

に着目する. self.0.borrow_mut()とは何だろうか. 理解するためには以下の2つを知る必要がある.

(1) 無名なフィールドは数字でアクセス出来る

Rustは, structのフィールドを無名にすることが出来る. これは, Tuple Structと呼ばれている. 以下のコードはコンパイル出来る.

struct A(i32, i32);
fn main() {
  let a = A(1, 2);
  println!("a.0={}, a.1={}", a.0, a.1);
}

(2) RcはDeref CoercionによってTになる

self.0の型はNodeRefである. それは, Rc<RefCell>である. borrow_mut()はRefCellのメソッドである. 一体どのようにしてRcを剥いでRefCellのメソッドにアクセスしたのか. その理由はDeref Coercion(https://doc.rust-lang.org/book/deref-coercions.html)である. RustはRcに対してDeref<Target=T>を実装している. だから, Rcは自動的にTとなり, Tのメソッドにアクセス出来る.

impl<T> Deref for Rc<T> where T: ?Sized

このように, Box, Rc, ArcなどDerefの実装されたポインタ風ラッパーは, それを含んだtype aliasで型を作って, 使う時にはmanualで剥ぐ操作をしないで使うというのがRustとして自然なコードの書き方なのだろうと思う.