[[📚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でイテレータやクロージャを使ったコードと使わないコードでパフォーマンスは変わらない]]