Rc

名前は、Reference Counted (参照カウント)の略。所有権を共有する為に使います。

Rc<T>はシングルスレッド用のカウンタを持つポインターでヒープ上で管理され、T値に対する複数の所有権(参照じゃない変数)を取得できます。ただし、Rc<T>だけで使う場合はTは読み込み専用的な扱いになります。

例えばStringで見てみると、この方はCopyを継承していないので、所有権は 1 つの変数しか持てません。

// Rust Playground
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=639fa328bea148a12b2130950f878216

fn main() {
    let str = String::from("foo");
     // --- move occurs because `str` has type `std::string::String`, 
     //     which does not implement the `Copy` trait
    let _a = str;
    //       --- value moved here
    let _b = str;
    //       ^^^ value used here after move
}

以下のように Rc で囲むと、これを回避できます。

// Rust Playground
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c8c9b503dc3d49e64d57361c1a2f356f

use std::rc::Rc;

fn main() {
    let rc_str = Rc::new(String::from("foo"));
    
    let _a = rc_str.clone();
    let _b = rc_str.clone();
}

RcTに変換できるDerefを実装してる為、デストラクタによって簡単にTに変換できます。

上記の_a_bは以下によって値が同じものだと分かります。

println!("{:p}", &*_a); // 0x55c9df807a70
println!("{:p}", &*_b); // 0x55c9df807a70

インスタンスメソッド

以下のようなものがあります。

  • clone 所有権の共有と参照カウンタの加算
  • strong_count 今の参照数を取得できます。
let thirty_three: Rc<i32> = Rc::new(33);

{
  let cloned = thirty_three.clone();

  // `thirty_three` と `cloned` は同じ
  assert!(thirty_three.eq(&cloned));

  assert_eq!(Rc::strong_count(&thirty_three), 2);

  // スコープを抜けるので参照カウンタを -1 する
}

assert_eq!(Rc::strong_count(&thirty_three), 1);