Open5

rust / スマートポインタ ( Rc Arc RefCell Box )

katayama8000katayama8000

Rc

  • ref

  • 語源

    • reference counter
    • 参照カウンター
    • スマートポインター
  • データの保持先

    • ヒープ
  • Memo

    • Rustでは、共有されているものには書き込めないので、Rcは不変
    • 書き込みたい時は RefCell を使う

サンプルコード

pub fn run() {
    let val = Rc::new(10);
    let val2 = val.clone();
    let val3 = val.clone();
    println!("val: {},", Rc::strong_count(&val));
    // reference counter is 3
    println!("val: {}", *val);
    // val is 10
}

使い所

  • コンパイル時にサイズが決まらない場合は、Boxを使うが、参照を共有したい場合は、Rcを使う
use std::rc::Rc;

pub fn run() {
    let a = Rc::new(List::Cons(5, Rc::new(List::Cons(10, Rc::new(List::Nil)))));
    let b = List::Cons(3, Rc::clone(&a));
    let c = List::Cons(4, Rc::clone(&a));
    println!("{:?}", c);
    // Cons(4, Cons(5, Cons(10, Nil)))
}

#[derive(Debug)]
enum List {
    Cons(i32, Rc<List>),
    Nil,
}
katayama8000katayama8000

RefCell

サンプルコード

可変参照と、不変参照を持ちながら、値を更新しようとしているのでコンパイルエラー

pub fn run() {
    let mut a = 10;
    let a1 = &mut a;
    let a2 = &a;
    *a1 = 10;
}

実行時にパニック

pub fn run() {
    let a = RefCell::new(10);
    let mut a1 = a.borrow_mut();
    let a2 = a.borrow();
    *a1 = 10;
}

Rc と 組み合わせると、内部可変にできる

use std::cell::RefCell;
use std::rc::Rc;

pub struct Test {
    x: i32,
}

impl Test {
    pub fn println(&self) {
        println!("{}", self.x);
    }
}

pub fn run() {
    let a = Rc::new(RefCell::new(Test { x: 11 }));
    a.borrow().println();
    let a1 = a.clone();
    let a2 = a.clone();

    a1.borrow_mut().x = 12;
    a2.borrow().println();
    // count of references
    println!("{}", Rc::strong_count(&a));
}

複数の参照がありながら、データを書き換えることができる

katayama8000katayama8000

Arc

  • 語源
    • Atomically Reference Counted
    • アトミック参照カウント
    • アトミック処理とは、他の処理に割り込まれない処理
  • データの保存先
    • ヒープ
  • MEMO
    • Rcと同じスマートポインター

Rc との違い

スレッド安全性
Rcだとスレッド間で値を共有しようとするとエラー

use std::rc::Rc;
use std::thread;

fn main() {
    let rc = Rc::new(42);
    let thread = thread::spawn(move || {
        //       ^^^^^^^^^^^^^ `std::rc::Rc<i32>` cannot be sent between threads safely
        eprintln!("value = {}", rc);
    });
    thread.join().unwrap();
}
use std::sync::Arc;
use std::thread;

fn main() {
    let rc = Arc::new(42);
    let thread = thread::spawn(move || {
        eprintln!("value = {}", rc);
    });
    thread.join().unwrap();
}
katayama8000katayama8000

Box

  • データの保存先
    • ヒープ
  • コンパイル時に、データの大きさが決まらない時に使う
katayama8000katayama8000

まとめ

Box<T>

  • 共有
  • 書き換え
    • ⭕️
  • 説明
    • ヒープにTのメモリを確保する

Rc<T>

  • 共有
    • ⭕️
  • 書き換え
  • 説明
    • ヒープに共有可能なTのメモリを確保する

RefCell<T>

  • 共有
  • 書き換え
    • ⭕️
  • 説明
    • コンパイル時ではなく、実行時に借用チェックをする

Rc<RefCell<T>>

  • 共有
    • ⭕️
  • 書き換え
    • ⭕️
  • 説明
    • ヒープに共有可能で、書き換え可能なメモリを確保する