クロージャトレイト

クロージャまたはラムダ式の型には名前を付けられません。しかし、これらは 特別な FnFnMut、および 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 によってキャプチャされたあらゆる値が消費されます。

FnMutFnOnce のサブタイプです。FnFnMutFnOnce のサブタイプです。 つまり、FnOnce が要求される場所であればどこでも FnMut を使え、FnMut または FnOnce が要求される場所であればどこでも Fn を使えます。

クロージャを受け取る関数を定義する際は、可能であれば FnOnce(つまり 1 回 呼び出す場合)を受け取り、そうでなければ FnMut、最後に Fn を受け取るように すべきです。これにより、呼び出し側に最大の柔軟性を与えられます。

これに対して、手元にあるクロージャについて最も柔軟なのは Fn (3 種類のクロージャトレイトのいずれを受け取る側にも渡せます)で、次が FnMut、最後が FnOnce です。

コンパイラは、クロージャが何をキャプチャするかに応じて、Copy (例: add_suffix)や Clone(例: take_and_reverse)も推論します。 関数ポインタ(fn 項目への参照)は CopyFn を実装します。