Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Rust辞典

  1. トップページ
  2. Rust辞典
  3. RefCell<T> / Cell<T>

RefCell<T> / Cell<T>

『RefCell<T>』と『Cell<T>』は内部可変性を提供するスマートポインタで、通常のRustの借用ルールを実行時にチェックすることで、不変参照を通じた値の変更を可能にします。

構文
use std::cell::{Cell, RefCell};

// RefCell:Copyでない型(String, Vec等)に使います。
let r = RefCell::new(値);
let borrowed = r.borrow();       // 不変借用(&T)
let mut borrowed = r.borrow_mut(); // 可変借用(&mut T)

// Cell:Copyトレイトを持つ型(i32, bool等)に使います。
let c = Cell::new(値);
c.set(新しい値);
c.get();
メソッド一覧
メソッド概要
RefCell::new(v)値vをRefCellでラップします。
borrow()不変借用(Ref<T>)を返します。可変借用中はpanic!します。
borrow_mut()可変借用(RefMut<T>)を返します。他の借用中はpanic!します。
try_borrow()借用に失敗してもpanic!せずResult<Ref<T>, BorrowError>を返します。
try_borrow_mut()可変借用をResult型で返します。失敗してもpanic!しません。
Cell::new(v)Copyトレイトのある値をCellでラップします。
cell.get()値のコピーを返します(Copyトレイトが必要)。
cell.set(v)値を置き換えます。借用なしで変更できます。
サンプルコード
use std::cell::{Cell, RefCell};

// 不変参照でも内部の値を変更できる構造体
struct Counter {
    count: Cell<u32>,
    log: RefCell<Vec<String>>,
}

impl Counter {
    fn new() -> Self {
        Counter {
            count: Cell::new(0),
            log: RefCell::new(vec![]),
        }
    }

    // &self(不変参照)でも内部を変更できます。
    fn increment(&self, label: &str) {
        let n = self.count.get() + 1;
        self.count.set(n);
        self.log.borrow_mut().push(format!("{}: {}", label, n));
    }

    fn show(&self) {
        for entry in self.log.borrow().iter() {
            println!("{}", entry);
        }
    }
}

fn main() {
    let c = Counter::new();
    c.increment("a"); // a: 1
    c.increment("b"); // b: 2
    c.increment("c"); // c: 3
    c.show();

    println!("total: {}", c.count.get()); // total: 3
}
概要

Rustのコンパイル時借用チェックは強力ですが、設計上どうしても不変参照から内部を変更したいケースがあります。『RefCell<T>』と『Cell<T>』はそのような「内部可変性」を提供します。借用ルールはコンパイル時ではなく実行時にチェックされます。

『Cell』はCopyトレイトを持つ軽量な型に使い、『RefCell』はStringやVecなどCopy不可の型に使います。どちらもシングルスレッド専用です。

RefCellの借用ルール違反(同時に可変借用と不変借用を取るなど)はコンパイルエラーではなく実行時panic!になります。境界が分かりにくい場合は安全な『try_borrow()』系メソッドを使ってください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。