RefCell<T>と内部可変性パターン

内部可変性は、そのデータへの不変参照がある時でさえもデータを可変化できるRustでのデザインパターンです: 普通、この行動は借用規則により許可されません。データを可変化するために、このパターンは、データ構造内でunsafeコードを使用して、 可変性と借用を支配するRustの通常の規則を捻じ曲げています。 unsafeコードは、コンパイラが規則をチェックしてくれることに頼らず、自分でチェックしていることをコンパイラに表明します; unsafeコードについては第19章で詳しく議論します。

たとえ、コンパイラが保証できなくても、借用規則に実行時に従うことが保証できる時に限り、 内部可変性パターンを使用した型を使用できます。関係するunsafeコードはそうしたら、安全なAPIにラップされ、 外側の型は、それでも不変です。

内部可変性パターンに従うRefCell<T>型を眺めてこの概念を探究しましょう。

RefCell<T>で実行時に借用規則を強制する

Rc<T>と異なり、RefCell<T>型は、保持するデータに対して単独の所有権を表します。では、 どうしてRefCell<T>Box<T>のような型と異なるのでしょうか?第4章で学んだ借用規則を思い出してください:

  • いかなる時点においても、1個の可変参照または任意個の不変参照のどちらか(両方ではない)が存在できる。
  • 参照は常に有効でなければならない。

参照とBox<T>では、借用規則の不変条件は、コンパイル時に強制されています。RefCell<T>では、 これらの不変条件は、実行時に強制されます。参照でこれらの規則を破ったら、コンパイルエラーになりました。 RefCell<T>でこれらの規則を破ったら、プログラムはパニックし、終了します。

コンパイル時に借用規則を精査することの利点は、エラーが開発過程の早い段階で捕捉されることと、 あらかじめ全ての分析が終わるので、実行パフォーマンスへの影響がないことです。それらの理由により、 多くの場合でコンパイル時に借用規則を精査することが最善の選択肢であり、これがRustの既定になっているのです。

借用規則を実行時に代わりに精査する利点は、コンパイル時の精査では許容されなかった特定のメモリ安全な筋書きが許容されることです。 Rustコンパイラのような静的解析は、本質的に保守的です。コードの特性には、コードを解析するだけでは検知できないものもあります: 最も有名な例は停止性問題であり、この本の範疇を超えていますが、調べると面白い話題です。

不可能な分析もあるので、Rustのコンパイラが、コードが所有権規則に応じていると確証を得られない場合、 正しいプログラムを拒否する可能性があります; このように、保守的なのです。コンパイラが不正なプログラムを受け入れたら、 ユーザは、コンパイラが行う保証を信じることはできなくなるでしょう。しかしながら、 コンパイラが正当なプログラムを拒否するのなら、プログラマは不便に思うでしょうが、悲劇的なことは何も起こり得ません。 コードが借用規則に従っているとプログラマは確証を得ているが、コンパイラがそれを理解し保証することができない時に RefCell<T>型は有用です。

Rc<T>と類似して、RefCell<T>もシングルスレッドの筋書きで使用するためのものであり、 試しにマルチスレッドの文脈で使ってみようとすると、コンパイルエラーを出します。 RefCell<T>の機能をマルチスレッドのプログラムで得る方法については、第16章で語ります。

こちらにBox<T>, Rc<T>, RefCell<T>を選択する理由を要約しておきます:

  • Rc<T>は、同じデータに複数の所有者を持たせてくれる; Box<T>RefCell<T>は単独の所有者。
  • Box<T>では、不変借用も可変借用もコンパイル時に精査できる; Rc<T>では不変借用のみがコンパイル時に精査できる; RefCell<T>では、不変借用も可変借用も実行時に精査される。
  • RefCell<T>は実行時に精査される可変借用を許可するので、RefCell<T>が不変でも、 RefCell<T>内の値を可変化できる。

不変な値の中の値を可変化することは、内部可変性パターンです。内部可変性が有用になる場面を見て、 それが可能になる方法を調査しましょう。

内部可変性: 不変値への可変借用

借用規則の結果は、不変値がある時、可変で借用することはできないということです。 例えば、このコードはコンパイルできません:

fn main() {
    let x = 5;
    let y = &mut x;
}

このコードをコンパイルしようとしたら、以下のようなエラーが出るでしょう:

$ cargo run
   Compiling borrowing v0.1.0 (file:///projects/borrowing)
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
(エラー: `x`は可変として宣言されていないので、可変として借用することはできません)
 --> src/main.rs:3:13
  |
3 |     let y = &mut x;
  |             ^^^^^^ cannot borrow as mutable
  |                   (可変として借用できません)
  |
help: consider changing this to be mutable
(ヘルプ: これを可変に変更することを考慮してください)
  |
2 |     let mut x = 5;
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `borrowing` (bin "borrowing") due to 1 previous error

ですが、メソッド内で値が自身を可変化するけれども、他のコードにとっては、 不変に見えることが有用な場面もあります。その値のメソッドの外のコードは、その値を可変化することはできないでしょう。 RefCell<T>を使うことは、内部可変性を取得する能力を得る1つの方法です。しかし、 RefCell<T>は借用規則を完全に回避するものではありません: コンパイラの借用チェッカーは、内部可変性を許可し、 借用規則は代わりに実行時に精査されます。この規則を侵害したら、コンパイルエラーではなくpanic!になるでしょう。

RefCell<T>を使用して不変値を可変化する実践的な例に取り組み、それが役に立つ理由を確認しましょう。

内部可変性のユースケース: モックオブジェクト

プログラマはテスト中に、特定の振る舞いを観測して、それが適切に実装されていることを確認するために、 ある型の代わりに他の型を使用することがあるでしょう。このプレースホルダ型はテストダブルと呼ばれます。 映画撮影において特定のトリッキーなシーンを演じるために、役者の代わりとして参加する人を指す、 「スタントダブル」と同じような意味で考えてください。テストダブルはテストの実行時に、他の型の代役を務めます。 モックオブジェクトは、テスト中に起きることを記録するテストダブルの特定の型なので、 正しい動作が起きたことをアサートできます。

Rustには、他の言語でいうオブジェクトは存在せず、また、他の言語のように標準ライブラリにモックオブジェクトの機能が組み込まれてもいません。 ですが、同じ目的をモックオブジェクトとして提供する構造体を作成することは確実にできます。

以下が、テストを行う筋書きです: 値を最大値に対して追跡し、現在値がどれくらい最大値に近いかに基づいてメッセージを送信するライブラリを作成します。 このライブラリは、ユーザが行うことのできるAPIコールの数の割り当てを追跡するのに使用することができるでしょう。

作成するライブラリは、値がどれくらい最大に近いかと、いつどんなメッセージになるべきかを追いかける機能を提供するだけです。 このライブラリを使用するアプリケーションは、メッセージを送信する機構を提供すると期待されるでしょう: アプリケーションは、アプリケーションにメッセージを置いたり、メールを送ったり、テキストメッセージを送るなどできるでしょう。 ライブラリはその詳細を知る必要はありません。必要なのは、提供するMessengerと呼ばれるトレイトを実装している何かなのです。 リスト15-20は、ライブラリのコードを示しています:

ファイル名: src/lib.rs

pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            //                  "エラー: 割り当てを超えています!"
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            //        "緊急警告: 割り当ての90%以上を使用してしまいました!"
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            //        "警告: 割り当ての75%以上を使用してしまいました!"
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}

リスト15-20: 値が最大値にどれくらい近いかを追跡し、特定のレベルの時に警告するライブラリ

このコードの重要な部分の1つは、Messengerトレイトには、selfへの不変参照とメッセージのテキストを取るsendというメソッドが1つあることです。 このトレイトが、モックが実際のオブジェクトと同じように使用できるために、モックオブジェクトが実装する必要のあるインターフェイスなのです。 もう1つの重要な部分は、LimitTrackerset_valueメソッドの振る舞いをテストしたいということです。value引数に渡すものを変えることができますが、 set_valueはアサートを行えるものは何も返してくれません。LimitTrackerMessengerトレイトを実装する何かと、 maxの特定の値で生成したら、valueに異なる数値を渡した時にメッセンジャーは適切なメッセージを送ると指示されると言えるようになりたいです。

sendを呼び出す時にメールやテキストメッセージを送る代わりに送ると指示されたメッセージを追跡するだけのモックオブジェクトが必要です。 モックオブジェクトの新規インスタンスを生成し、モックオブジェクトを使用するLimitTrackerを生成し、 LimitTrackerset_valueを呼び出し、それからモックオブジェクトに期待しているメッセージがあることを確認できます。 リスト15-21は、それだけをするモックオブジェクトを実装しようとするところを示しますが、借用チェッカーが許可してくれません:

ファイル名: src/lib.rs

pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    struct MockMessenger {
        sent_messages: Vec<String>,
    }

    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                sent_messages: vec![],
            }
        }
    }

    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            self.sent_messages.push(String::from(message));
        }
    }

    #[test]
    fn it_sends_an_over_75_percent_warning_message() {
        let mock_messenger = MockMessenger::new();
        let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

        limit_tracker.set_value(80);

        assert_eq!(mock_messenger.sent_messages.len(), 1);
    }
}

リスト15-21: 借用チェッカーが許可してくれないMockMessengerを実装しようとする

このテストコードはStringVecで送信すると指示されたメッセージを追跡するsent_messagesフィールドのあるMockMessenger構造体を定義しています。 また、空のメッセージリストから始まる新しいMockMessenger値を作るのを便利にしてくれる関連関数のnewも定義しています。 それからMockMessengerMessengerトレイトを実装しているので、LimitTrackerMockMessengerを与えられます。 sendメソッドの定義で引数として渡されたメッセージを取り、sent_messagesMockMessengerリストに格納しています。

テストでは、max値の75%以上になる何かにvalueをセットしろとLimitTrackerが指示される時に起きることをテストしています。 まず、新しいMockMessengerを生成し、空のメッセージリストから始まります。そして、 新しいLimitTrackerを生成し、新しいMockMessengerの参照と100というmax値を与えます。 LimitTrackerset_valueメソッドは80という値で呼び出し、これは100の75%を上回っています。 そして、MockMessengerが追いかけているメッセージのリストが、今は1つのメッセージを含んでいるはずとアサートします。

ところが、以下のようにこのテストには1つ問題があります:

$ cargo test
   Compiling limit-tracker v0.1.0 (file:///projects/limit-tracker)
error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a `&` reference
(エラー: `self.sent_messages`は`&`参照の後ろにあるので、可変として借用できません)
  --> src/lib.rs:58:13
   |
58 |             self.sent_messages.push(String::from(message));
   |             ^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |                               (`self`は`&`参照なので、それが参照するデータは可変として借用できません)
   |
help: consider changing this to be a mutable reference
(ヘルプ: 可変参照に変更することを検討してください)
   |
2  |     fn send(&mut self, msg: &str);
   |             ~~~~~~~~~

For more information about this error, try `rustc --explain E0596`.
error: could not compile `limit-tracker` (lib test) due to 1 previous error

sendメソッドはselfへの不変参照を取るので、MockMessengerを変更してメッセージを追跡できないのです。 代わりに&mut selfを使用するというエラーテキストからの提言を選ぶこともできないのです。 そうしたら、sendのシグニチャが、Messengerトレイト定義のシグニチャと一致しなくなるからです(気軽に試してエラーメッセージを確認してください)。

これは、内部可変性が役に立つ場面なのです!sent_messagesRefCell<T>内部に格納し、 そうしたらsendメソッドは、sent_messagesを変更して見かけたメッセージを格納できるようになるでしょう。 リスト15-22は、それがどんな感じかを示しています:

ファイル名: src/lib.rs

pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;

    struct MockMessenger {
        sent_messages: RefCell<Vec<String>>,
    }

    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                sent_messages: RefCell::new(vec![]),
            }
        }
    }

    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            self.sent_messages.borrow_mut().push(String::from(message));
        }
    }

    #[test]
    fn it_sends_an_over_75_percent_warning_message() {
        // --snip--
        let mock_messenger = MockMessenger::new();
        let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

        limit_tracker.set_value(80);

        assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
    }
}

リスト15-22: 外側の値は不変と考えられる一方でRefCell<T>で内部の値を可変化する

さて、sent_messagesフィールドは、Vec<String>ではなく、型RefCell<Vec<String>>になりました。 new関数で、空のベクタの周りにRefCell<Vec<String>>を新しく作成しています。

sendメソッドの実装については、最初の引数はそれでもselfへの不変借用で、トレイト定義と合致しています。 RefCell<Vec<String>>borrow_mutself.sent_messagesに呼び出し、 RefCell<Vec<String>>の中の値への可変参照を得て、これはベクタになります。 それからベクタへの可変参照にpushを呼び出して、テスト中に送られるメッセージを追跡しています。

行わなければならない最後の変更は、アサート内部にあります: 内部のベクタにある要素の数を確認するため、 RefCell<Vec<String>>borrowを呼び出し、ベクタへの不変参照を得ています。

RefCell<T>の使用法を見かけたので、動作の仕方を深掘りしましょう!

RefCell<T>で実行時に借用を追いかける

不変および可変参照を作成する時、それぞれ&&mut記法を使用します。RefCell<T>では、 borrowborrow_mutメソッドを使用し、これらはRefCell<T>に所属する安全なAPIの一部です。 borrowメソッドは、スマートポインタ型のRef<T>を返し、borrow_mutはスマートポインタ型のRefMut<T>を返します。 どちらの型もDerefを実装しているので、普通の参照のように扱うことができます。

RefCell<T>は、現在活動中のRef<T>RefMut<T>スマートポインタの数を追いかけます。 borrowを呼び出す度に、RefCell<T>は活動中の不変参照の数を増やします。Ref<T>の値がスコープを抜けたら、 不変参照の数は1下がります。コンパイル時の借用規則と全く同じように、RefCell<T>はいかなる時も、 複数の不変借用または1つの可変借用を持たせてくれるのです。

これらの規則を侵害しようとすれば、参照のようにコンパイルエラーになるのではなく、 RefCell<T>の実装は実行時にパニックするでしょう。リスト15-23は、リスト15-22のsend実装に対する変更を示しています。 同じスコープで2つの可変借用が活動するようわざと生成し、RefCell<T>が実行時にこれをすることを阻止してくれるところを説明しています。

ファイル名: src/lib.rs

pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;

    struct MockMessenger {
        sent_messages: RefCell<Vec<String>>,
    }

    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                sent_messages: RefCell::new(vec![]),
            }
        }
    }

    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            let mut one_borrow = self.sent_messages.borrow_mut();
            let mut two_borrow = self.sent_messages.borrow_mut();

            one_borrow.push(String::from(message));
            two_borrow.push(String::from(message));
        }
    }

    #[test]
    fn it_sends_an_over_75_percent_warning_message() {
        let mock_messenger = MockMessenger::new();
        let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

        limit_tracker.set_value(80);

        assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
    }
}

リスト15-23: 同じスコープで2つの可変参照を生成してRefCell<T>がパニックすることを確かめる

borrow_mutから返ってきたRefMut<T>スマートポインタに対して変数one_borrowを生成しています。 そして、同様にして変数two_borrowにも別の可変借用を生成しています。これにより同じスコープで2つの可変参照ができ、 これは許可されないことです。このテストを自分のライブラリ用に走らせると、リスト15-23のコードはエラーなくコンパイルできますが、 テストは失敗するでしょう:

$ cargo test
   Compiling limit-tracker v0.1.0 (file:///projects/limit-tracker)
    Finished test [unoptimized + debuginfo] target(s) in 0.91s
     Running unittests src/lib.rs (target/debug/deps/limit_tracker-e599811fa246dbde)

running 1 test
test tests::it_sends_an_over_75_percent_warning_message ... FAILED

failures:

---- tests::it_sends_an_over_75_percent_warning_message stdout ----
thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at src/lib.rs:60:53:
already borrowed: BorrowMutError
(スレッド'tests::it_sends_an_over_75_percent_warning_message'は、src/lib.rs:60:53でパニックしました:
すでに借用されています: BorrowMutError)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_sends_an_over_75_percent_warning_message

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

コードは、already borrowed: BorrowMutErrorというメッセージとともにパニックしたことに注目してください。 このようにしてRefCell<T>は実行時に借用規則の侵害を扱うのです。

ここで行ったように、コンパイル時ではなく実行時に借用エラーをキャッチすることを選択するということは、 開発過程のより遅い段階でコードのミスを発見することになる可能性があることを意味します: コードをプロダクションにデプロイする時までに発見できないかもしれません。また、 コンパイル時ではなく、実行時に借用を追いかける結果として、少し実行時にパフォーマンスを犠牲にするでしょう。 しかしながら、RefCell<T>を使うことで、不変値のみが許可される文脈で使用しつつ、 自身を変更して見かけたメッセージを追跡するモックオブジェクトを書くことが可能になります。 代償はありますが、RefCell<T>を使用すれば、普通の参照よりも多くの機能を得ることができるわけです。

Rc<T>RefCell<T>を組み合わせることで可変なデータに複数の所有者を持たせる

RefCell<T>の一般的な使用法は、Rc<T>と組み合わせることにあります。Rc<T>は何らかのデータに複数の所有者を持たせてくれるけれども、 そのデータに不変のアクセスしかさせてくれないことを思い出してください。RefCell<T>を抱えるRc<T>があれば、 複数の所有者を持ちそして、可変化できる値を得ることができるのです。

例を挙げれば、Rc<T>を使用して複数のリストに別のリストの所有権を共有させたリスト15-18のコンスリストの例を思い出してください。 Rc<T>は不変値だけを抱えるので、一旦生成したら、リストの値はどれも変更できません。RefCell<T>を含めて、 リストの値を変更する能力を得ましょう。RefCell<T>Cons定義で使用することで、 リスト全てに格納されている値を変更できることをリスト15-24は示しています:

ファイル名: src/main.rs

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

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let value = Rc::new(RefCell::new(5));

    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

    let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
    let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

    *value.borrow_mut() += 10;

    println!("a after = {:?}", a);
    println!("b after = {:?}", b);
    println!("c after = {:?}", c);
}

リスト15-24: Rc<RefCell<i32>>で可変化できるListを生成する

Rc<RefCell<i32>>のインスタンスの値を生成し、valueという名前の変数に格納しているので、 直接後ほどアクセスすることができます。そして、avalueを持つCons列挙子でListを生成しています。 valueからaに所有権を移したり、avalueから借用するのではなく、avalueどちらにも中の5の値の所有権を持たせるよう、 valueをクローンする必要があります。

リストaRc<T>に包んでいるので、リストbcを生成する時に、どちらもaを参照できます。 リスト15-18ではそうしていました。

abcのリストを作成した後、valueの値に10を足したいとします。これをvalueborrow_mutを呼び出すことで行い、 これは、第5章で議論した自動参照外し機能(->演算子はどこに行ったの?」節をご覧ください)を使用して、 Rc<T>を内部のRefCell<T>値に参照外ししています。borrow_mutメソッドは、 RefMut<T>スマートポインタを返し、それに対して参照外し演算子を使用し、中の値を変更します。

abcを出力すると、全て5ではなく、変更された15という値になっていることがわかります。

$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
    Finished dev [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/cons-list`
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil))

このテクニックは非常に綺麗です!RefCell<T>を使用することで表面上は不変なList値を持てます。 しかし、内部可変性へのアクセスを提供するRefCell<T>のメソッドを使用できるので、必要な時にはデータを変更できます。 借用規則を実行時に精査することでデータ競合を防ぎ、時としてデータ構造でちょっとのスピードを犠牲にこの柔軟性を得るのは価値があります。 RefCell<T>は、マルチスレッドで実行されるコードに対しては機能しないことに注意してください! RefCell<T>のスレッドセーフなバージョンはMutex<T>で、Mutex<T>については第16章で議論しましょう。