目次

Rustの勉強[RefCell<T> その2]

(更新: )ぎじゅつ

はじめに

#

NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる

お勉強

#
  • さてやるか

  • もんだい、RefCellの特徴2つ

  • 前回はこれらしい

    • 何だっけ
    • シングルスレッド用
    • 所有権をもつ
    • だったはず

NO IMAGERefCell<T>と内部可変性パターン - The Rust Programming Language 日本語版

  • さてここから

  • お、問題の答えも思い出したな

    • 不変を可変にできるやつや

メモ

#
  • エラー見てた
#[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);
    }
}
  • fn send(&self, message: &str)

    • ここか
    • ここで不変として引数にしているのに、pushメソッドで内部を変更してしまっているからダメなんだな
    • んてRefCellの機能が不変を可変として扱うことか
  • 理解してきたけど普通にダメそうだな

  • んーなんかいっぱい文字書いてあるけど、これってRustの基本実装でどうしても表現できないときしか使わないほうがいいよな

#[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--

        assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
    }
}
  • なるほどね、Vec側を囲むわけか

RefCell<T>では、 borrowborrow_mutメソッドを使用し、これらはRefCell<T>に所属する安全なAPIの一部です。 borrowメソッドは、スマートポインタ型のRef<T>を返し、borrow_mutはスマートポインタ型のRefMut<T>を返します。

  • ワロタ
    • 型いっぱい出てくるじゃん、バカヤロウ

Derefを実装しているので、普通の参照のように扱うことができます。

  • なるほどね

  • あーこのメソッドは選択して不変参照か、可変参照か選べるのか

  • 全体的にDerefトレイトがだいじ

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));
    }
}
$ 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`
  • んーなるほど
  • 1回しか、借用できないけどビルドは通ってしまうわけね

ここで行ったように、コンパイル時ではなく実行時に借用エラーをキャッチすることを選択するということは、 開発過程のより遅い段階でコードのミスを発見することになる可能性があることを意味します

  • まあそうだよね
  • buildで分かるからRustがいいわけだしね
  • 時間切れ

まとめ

#
  • 時間かけているるけどまぁ使わなさそうだなと思う
  • こんだけたいへんだからbuildで怒られようって啓示かもね
  • Derefを実装しているのはRefCellじゃないんだと思う

NO IMAGERefCell in std::cell - RustA mutable memory location with dynamically checked borrow rules

  • やっぱりね

次ここ
NO IMAGERefCell<T>と内部可変性パターン - The Rust Programming Language 日本語版

RefCellから普遍や可変参照する方法