Open5
rust / スマートポインタ ( Rc Arc RefCell Box )
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,
}
RefCell
- ref
- コンパイル時ではなく、実行時に borrow check される
サンプルコード
可変参照と、不変参照を持ちながら、値を更新しようとしているのでコンパイルエラー
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));
}
複数の参照がありながら、データを書き換えることができる
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();
}
Box
- データの保存先
- ヒープ
- コンパイル時に、データの大きさが決まらない時に使う
まとめ
Box<T>
- 共有
- ❌
- 書き換え
- ⭕️
- 説明
- ヒープにTのメモリを確保する
Rc<T>
- 共有
- ⭕️
- 書き換え
- ❌
- 説明
- ヒープに共有可能なTのメモリを確保する
RefCell<T>
- 共有
- ❌
- 書き換え
- ⭕️
- 説明
- コンパイル時ではなく、実行時に借用チェックをする
Rc<RefCell<T>>
- 共有
- ⭕️
- 書き換え
- ⭕️
- 説明
- ヒープに共有可能で、書き換え可能なメモリを確保する