目次

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

(更新: )ぎじゅつ

はじめに

#

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

  • 光らせるのを習慣化したい
  • 前回の振り返りテスト作るか
  • ごちゃごちゃやったが、ひとつの値に対して複数のポインタを当てることができないので、Rcとできるという話だった

お勉強

#

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

  • 今日はここ

メモ

#
  • とりあえずざっくり読む
    • そこそこ文章量がある
    • んーとりあえず借用を規制するらしい

このパターンは、データ構造内でunsafeコードを使用して、 可変性と借用を支配するRustの通常の規則を捻じ曲げています。

  • なんか基本使っちゃダメそう

たとえ、コンパイラが保証できなくても、借用規則に実行時に従うことが保証できる時に限り、 内部可変性パターンを使用した型を使用できます。

  • んーどうなんだろう、グレー

Rc<T>と異なり、RefCell<T>型は、保持するデータに対して単独の所有権を表します。

  • とりあえず所有権は複数持たないので、moveはしそう

参照でこれらの規則を破ったら、コンパイルエラーになりました。 RefCell<T>でこれらの規則を破ったら、プログラムはパニックし、終了します。

  • 駄目じゃん
    • と思ったけど処置はできるのか
    • うーん、使える。。。?

Rc<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
  • そのまんまの意味だな

    • mutだし
  • んーなるほど

  • ここでは借用チェッカーに引っかからないからpanic!が発生する状況を教えてくれそうだな

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!");
        }
    }
}
  • お、ユースケースがテストなのか

  • flaykyにならなければいいかもね

#[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);
    }
}
  • これがテストか
  • エラーが起きるらしい、どこだ
$ 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

まとめ

#
  • 時間切れ
  • 不変の借用を可変に参照したい
  • 借用チェッカーに引っかからないけどミスるとpanic!する
  • シングルスレッドのみ
  • というところまでわかった
  • あとRcと違って所有権をもつらしい

次はエラーの理解から
NO IMAGERefCell<T>と内部可変性パターン - The Rust Programming Language 日本語版

もんだい、RefCellの特徴2つ