強参照Rc<T> と弱参照Weak<T>

std::rc::Rc<T> 概要

動的な参照を実現。&演算子による参照がコンパイル時にされるのに対して、Rc<T>は実行時に参照を確保する。 これによってライフタイムを除去することができる。

ライフタイムの除去

参照による共有

借用チェッカーを通すために、ライフタイムを付与する必要がある

struct A<'a> {
    data: &'a [u8],
}
impl<'a> A<'a> {
    fn new(data: &[u8]) -> A {
        A {
            data: data,
        }
    }
}

// 1k bytes
let big_data = (0..1000).collect::<Vec<u8>>();
let a1 = A::new(&big_data); // ビッグデータの共有1
let a2 = A::new(&big_data); // ビッグデータの共有2

Rc<T>による共有

// ライフタイムの除去
struct B {
    data: Rc<Vec<u8>>,
}
impl B {
    fn new(data: Rc<Vec<u8>>) -> B {
        B {
            data: data,
        }
    }
}

// Rcに包む
let shared_data = Rc::new(big_data);
let b1 = B::new(shared_data);
let b2 = B::new(Rc::clone(&shared_data));
  • Rc::clone(&b1) or b1.clone() によって参照の共有を作っていく

強参照(std::rc::Rc<T>)と弱参照(std::rc::Weak<T>)

Rc::cloneを使用すると、強参照(Rc<T>)を生成するため、互いに参照し合うと、メモリーリークを発生させる。 この場合は、Rc::downgradeを使用することで、弱参照(Weak<T>)を生成する。

let p = Parent::new();
let p = Rc::new(p);

// 弱参照でparentと結ぶ
let c1 = Child::new(Rc::downgrade(&p), 1);
let c2 = Child::new(Rc::downgrade(&p), 2);
let c3 = Child::new(Rc::downgrade(&p), 3);

println!("{}", Rc::strong_count(&p)); // 1

// 強参照でchildを渡す
p.add_child(Rc::new(c1));
p.add_child(Rc::new(c2));
p.add_child(Rc::new(c3));