[[📚The Rust Programming Language|the book]]の第13章。 ## クロージャ: 環境をキャプチャできる匿名関数 - [[クロージャ (Rust)|クロージャ]] ```rust use std::thread; use std::time::Duration; fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); intensity } fn main() { let r = simulated_expensive_calculation(180702); eprintln!("r = {:?}", r); } ``` 以下のコードの戻り値がなぜか空タプルと推論される。`num`はu32と推論されている。 ```rust let expensive_closure = |num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }; ``` [[IntelliJ Rust]]の問題ぽい。[[📝IntelliJ Rustでクロージャの戻り値型を省略すると推論が正しく行われない]] で調べたが、最新バージョンにするだけでは解決しなかった。 - [[クロージャ (Rust)|クロージャ]]は引数や戻り値の型を注釈する必要がない - 公開するためのものではないから - [[Fnトレイト]]、[[FnMutトレイト]]、[[FnOnceトレイト]] [[パターンマッチ (Rust)|パターンマッチ]]は以下で書き換えもできる。 ```rust self.value.unwrap_or_else(|| { let v = (self.calculation)(arg); self.value = Some(v); v }) ``` ただ以下はダメ。`unwrap`の前に`_or`の先が計算されてしまうので、毎回処理が発生するから。 ```rust self.value.unwrap_or({ let v = (self.calculation)(arg); self.value = Some(v); v }) ``` 環境からの値のキャプチャ。 | [[トレイト]] | 渡すもの | | ------------------ | ------------ | | [[FnOnceトレイト]] | [[所有権]] | | [[FnMutトレイト]] | [[可変参照]] | | [[Fnトレイト]] | [[不変参照]] | 基本は[[FnOnceトレイト]]を実装し、[[所有権]]を渡す必要がない場合は[[FnMutトレイト]]を、[[可変参照]]を渡す必要がない場合は[[Fnトレイト]]をも実装する。つまり、3つとも実装している場合が一番制約が強い。 [[所有権]]を奪うことを強制する場合は[[moveクロージャ]]を使う。 ## 一連の要素をイテレータで処理する - [[関連型]] - [[iter()]] - `next`で得られる値は[[不変参照]] - [[iter_mut()]] - `next`で得られる値は[[可変参照]] - [[into_iter()]] - `next`で得られる値は[[所有権]]が[[ムーブ]]した値 - [[消費アダプタ]] - [[イテレータアダプタ]] ```rust struct Counter { count: u32, } impl Counter { fn new() -> Counter { Counter { count: 0 } } } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> { self.count += 1; if self.count < 6 { Some(self.count) } else { None } } } fn main() { let iter = Counter::new(); for i in iter { eprintln!("i = {:?}", i); } } ``` ```rust // [1, 2, 3, 4, 5] let sum: u32 = Counter::new() // [2, 3, 4, 5] .zip(Counter::new().skip(1)) // [(1, 2), (2, 3), (3, 4), (4, 5)] .map(|(a, b)| a * b) // [2, 6, 12, 20] .filter(|x| x % 3 == 0) // [6, 12] .sum(); // 18 ``` ## 入出力プロジェクトを改善する [[let-else]]構文がここでも使える。 ```rust impl Config { pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> { args.next(); let Some(query) = args.next() else { return Err("Didn't get a query string") }; let Some(filename) = args.next() else { return Err("Didn't get a filename") }; let case_sensitive = env::var("CASE_INSENSITIVE").is_err(); Ok(Config { query, filename, case_sensitive, }) } } ``` 可変な状態を減らすと同時実行性が高まる。 ```rust pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents.lines().filter(|x| x.contains(query)).collect() } pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let query = query.to_lowercase(); contents .lines() .filter(|x| x.to_lowercase().contains(&query)) .collect() } ``` ## パフォーマンス比較: ループVSイテレータ - 速度は一緒 - [[ゼロコスト抽象化]] - 余談: [[ゼロオーバーヘッド原則]] - [[ループの展開]] - [[Rustでイテレータやクロージャを使ったコードと使わないコードでパフォーマンスは変わらない]]