目次

Rustの勉強[クロージャ その2]

(更新: )ぎじゅつ

はじめに

#

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

  • 土曜日に何もやる気がせず、すべてをサボった
  • 土日のどっちかはこれにしようかな
  • これをしても落ち込まなくなったのがめちゃめちゃ成長

お勉強

#

メモ

#

NO IMAGEクロージャ:環境をキャプチャする匿名関数 - The Rust Programming Language 日本語版

  • これをやる
  • クロージャは型推論があるらしい
  • たしかに型を書いてなかった気がする

このような小さく匿名の関数で型をプログラマに注釈させることは煩わしいし、コンパイラがすでに利用可能な情報と大きく被っています。
本当に必要な以上に冗長になることと引き換えに、明示性と明瞭性を向上させたいなら、変数に型注釈を加えることもできます; リスト13-5で定義したクロージャに型を注釈するなら、リスト13-7に示した定義のようになるでしょう。

  • まあそうなるよね
  • だからここで言いたいのはクロージャなんか使うときは型を明示しないといけない処理を書くなってことだと思ってる
let expensive_closure = |num: u32| -> u32 {
    println!("calculating slowly...");
    thread::sleep(Duration::from_secs(2));
    num
};
  • まあなんというか予想通りだな

型注釈を付け加えると、クロージャの記法は、関数の記法により酷似して見えます。
以下が、引数に1を加える関数の定義と、 同じ振る舞いをするクロージャの定義の記法を縦に比べたものです。

let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;
  • なんだよこれ
  • めんどくさ
let example_closure = |x| x;

let s = example_closure(String::from("hello"));
let n = example_closure(5);
error[E0308]: mismatched types
 --> src/main.rs
  |
  | let n = example_closure(5);
  |                         ^ expected struct `std::string::String`, found
  integral variable
  |
  = note: expected type `std::string::String`
             found type `{integer}`
  • 先に推論したもので確定しちゃうんだな
  • 先出しが勝つ

メモ化(memoization)または、 遅延評価(lazy evaluation)として知っているかもしれません。

  • この前に長文があったけど何いっているかわからなかった

ジェネリック引数とFnトレイトを使用してクロージャを保存する

  • これがタイトルだからクロージャを保存する方法か

クロージャやクロージャの呼び出し結果の値を保持する構造体を作れるのです。
結果の値が必要な場合のみにその構造体はクロージャを実行し、その結果の値をキャッシュするので、残りのコードは、 結果を保存し、再利用する責任を負わなくて済むのです。

  • あー意味がわかった
  • 関数が必要な値を初回はクロージャを通して、2回目以降はキャッシュで取り出せるって話ね
struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}
  • やべ忘れてたけどtrait boundってstructにもできるんだっけ
impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}
  • あーはいはい、なるほどね
  • traitにvalue関数を実装するときにOptionをmatchすることでNoneの場合だけ関数を使えるのか
  • そら動くわ
  • ん???
  • でもこれ普通だからクロージャ関係なくない?
fn generate_workout(intensity: u32, random_number: u32) {
    let mut expensive_result = Cacher::new(|num| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_result.value(intensity));
        println!("Next, do {} situps!", expensive_result.value(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!(
                "Today, run for {} minutes!",
                expensive_result.value(intensity)
            );
        }
    }
}
  • おお、こういうふうにstruct使えってことか
  • たしかーーーに

まとめ

#
  • クロージャでの型推論は先に動いたもの依存
  • そもそもクロージャで型を定義したいっていう思想があんまりよくなさそう
  • structに型を定義してキャッシュを効かせる実装があって天才となった
    • 感動していたのに次のタイトルがCacherの限界になっている

でも次これ

NO IMAGEクロージャ:環境をキャプチャする匿名関数 - The Rust Programming Language 日本語版