やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのスマートポインタ(Rc<T>)

 複数の所有者をもたせる。参照カウント式。

成果物

複数から所有不可

main.rs

#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}
use List::{Cons, Nil};
fn main() {
    let a = Cons(1,
        Box::new(Cons(2,
            Box::new(Cons(3,
                Box::new(Nil))))));
    let b = Cons(4, Box::new(a));
    let c = Cons(5, Box::new(a));
}
$ rustc main.rs
error[E0382]: use of moved value: `a`
  --> main.rs:17:30
   |
16 |     let b = Cons(4, Box::new(a));
   |                              - value moved here
17 |     let c = Cons(5, Box::new(a));
   |                              ^ value used here after move
   |
   = note: move occurs because `a` has type `List`, which does not implement the `Copy` trait

 aの所有権はすでにbへムーブされた。その後にaを参照したためエラー。

Rc<T>で複数に所有させる

#[derive(Debug)]
enum List {
    Cons(i32, Rc<List>),
    Nil,
}
use List::{Cons, Nil};
use std::rc::Rc;
fn main() {
    let a = Rc::new(Cons(1,
                Rc::new(Cons(2,
                    Rc::new(Cons(3,
                        Rc::new(Nil)))))));
    let b = Cons(4, Rc::clone(&a));
    let c = Cons(5, Rc::clone(&a));
//    println!("{:?}", list);
}
  • use std::rc::Rc;追加
  • 変数aの値をConsからRc<Cons>に変更
  • Rc::clone()で複数所有させる

Rc::clone()

 clone関数の実装はふつうディープコピーである。だが、Rc型のそれは参照カウンタをインクリメントするだけ。

参照カウンタ

 aの参照カウンタを見てみる。

fn main() {
    let a = Rc::new(Cons(1,
                Rc::new(Cons(2,
                    Rc::new(Cons(3,
                        Rc::new(Nil)))))));
    println!("{}", Rc::strong_count(&a));
    let b = Cons(4, Rc::clone(&a));
    println!("{}", Rc::strong_count(&a));
    {
        let c = Cons(5, Rc::clone(&a));
        println!("{}", Rc::strong_count(&a));
    }
    println!("{}", Rc::strong_count(&a));
}
$ ./main
1
2
3
2
  • Rc::clone()したら増加
  • スコープを抜けたら減少

 main関数のスコープが終了すると、c,b,adropされていく。aが指す値への参照カウンタが減っていく。最後にはゼロになってメモリ解放される。

所感

 これ、すごく気になってた。Rustでは「所有権がひとつだけ」と言うが、それでは複数もたせられず、複数から変更をかけられないじゃん! と思ってた。でもRc<T>を使えばできる。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで