std::marker::Copy
未実装な型でも扱えるCell<T>
(内側のT
が可変)です。
RefCell
では::borrow
で借用、::borrow_mut
でミュータブル借用が行えます。つまりCell
の::set
にあたるものは無く、ミュータブル借用で取得した値を直接更新します。
::borrow
の戻り値はRef<T>
となりますが、これはDeref
が実装されている為T
に変換可能です。
以下はコンパイルできる適当なコードです。
use std::cell::RefCell;
let ref_cell_string: RefCell = RefCell::new("foo".into());
let ref_ref_cell_string = &ref_cell_string;
*ref_ref_cell_string.borrow_mut() += "bar";
{
let ref_ref_cell_string_in_scope = &ref_cell_string;
*ref_ref_cell_string_in_scope.borrow_mut() += "baz";
}
assert_eq!(*ref_cell_string.borrow(), "foobarbaz".to_string());
以下はコンパイルできない Panic を起こすコードです。違いは::borrow_mut
を一旦変数に置いている点だけです。
use std::cell::RefCell;
let ref_cell_string: RefCell = RefCell::new("foo".into());
let ref_ref_cell_string = &ref_cell_string;
let mut _borrowed = ref_ref_cell_string.borrow_mut();
*_borrowed += "bar";
{
// snap
}
assert_eq!(*ref_cell_string.borrow(), "foobarbaz".to_string());
これはRefCell
の::borrow
や::borrow_mut
による借用にも Rust の基本的な借用ルールが適用される為です。それは、
イミュータブル参照であればいくらでも大丈夫
ミュータブル参照は1つだけにしてね
というようなものでした。
上記の場合_borrowed
がミュータブル参照としてスコープにある状態で、ref_cell_string.borrow()
によるイミュータブル参照を取ろうとした為 Panic してしまいました。
直すには借用ルールと同じようにミュータブル参照を扱う部分を小さなスコープ化に閉じ込めてしまえば大丈夫です。
use std::cell::RefCell;
let ref_cell_string: RefCell = RefCell::new("foo".into());
let ref_ref_cell_string = &ref_cell_string;
{
let mut _borrowed = ref_ref_cell_string.borrow_mut();
*_borrowed += "bar";
}
{
let ref_ref_cell_string_in_scope = &ref_cell_string;
let mut _borrowed = ref_ref_cell_string_in_scope.borrow_mut();
*_borrowed += "baz";
}
assert_eq!(*ref_cell_string.borrow(), "foobarbaz".to_string());
先程 Panic が起きてしまったと言ったようにRefCell
はコンパイル時にルールが守られているか確認していません。全ては実行時に初めて確認されています。