クロージャトレイト
クロージャまたはラムダ式の型には名前を付けられません。しかし、これらは 特別な Fn、 FnMut、および FnOnce トレイトを実装しています。
特別な型 fn(..) -> T は関数ポインタを表します。これは、関数のアドレス、 または何もキャプチャしないクロージャのいずれかです。
// 著作権 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 fn apply_and_log( func: impl FnOnce(&'static str) -> String, func_name: &'static str, input: &'static str, ) { println!("Calling {func_name}({input}): {}", func(input)) } fn main() { let suffix = "-itis"; let add_suffix = |x| format!("{x}{suffix}"); apply_and_log(&add_suffix, "add_suffix", "senior"); apply_and_log(&add_suffix, "add_suffix", "appendix"); let mut v = Vec::new(); let mut accumulate = |x| { v.push(x); v.join("/") }; apply_and_log(&mut accumulate, "accumulate", "red"); apply_and_log(&mut accumulate, "accumulate", "green"); apply_and_log(&mut accumulate, "accumulate", "blue"); let take_and_reverse = |prefix| { let mut acc = String::from(prefix); acc.push_str(&v.into_iter().rev().collect::<Vec<_>>().join("/")); acc }; apply_and_log(take_and_reverse, "take_and_reverse", "reversed: "); }
Fn(例: add_suffix)は、キャプチャした値を消費も変更もしません。クロージャへの 共有参照だけで呼び出せるため、このクロージャは繰り返し実行でき、同時に 実行することさえできます。
FnMut(例: accumulate)は、キャプチャした値を変更する可能性があります。 クロージャオブジェクトには排他的参照を通してアクセスするため、繰り返し 呼び出せますが、同時には呼び出せません。
FnOnce(例: take_and_reverse)を持っている場合、それを呼び出せるのは 1 回だけです。 そうすると、クロージャと、move によってキャプチャされたあらゆる値が消費されます。
FnMut は FnOnce のサブタイプです。Fn は FnMut と FnOnce のサブタイプです。 つまり、FnOnce が要求される場所であればどこでも FnMut を使え、FnMut または FnOnce が要求される場所であればどこでも Fn を使えます。
クロージャを受け取る関数を定義する際は、可能であれば FnOnce(つまり 1 回 呼び出す場合)を受け取り、そうでなければ FnMut、最後に Fn を受け取るように すべきです。これにより、呼び出し側に最大の柔軟性を与えられます。
これに対して、手元にあるクロージャについて最も柔軟なのは Fn (3 種類のクロージャトレイトのいずれを受け取る側にも渡せます)で、次が FnMut、最後が FnOnce です。
コンパイラは、クロージャが何をキャプチャするかに応じて、Copy (例: add_suffix)や Clone(例: take_and_reverse)も推論します。 関数ポインタ(fn 項目への参照)は Copy と Fn を実装します。