2.3 闭包 Trait

03-闭包 Trait — Comprehensive Rust

译文 · 基于 Comprehensive Rust

原文链接: https://google.github.io/comprehensive-rust/closures/traits.html

2.3 闭包 Trait

闭包或 lambda 表达式的类型无法命名。不过它们实现了特殊的 Fn、 FnMut 与 FnOnce trait:

特殊类型 fn(..) -> T 指函数指针——要么是函数地址,要么是不捕获任何东西的闭包。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Copyright 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),只能调用一次。这样做会消费闭包以及按移动捕获的任何值。

FnMut 是 FnOnce 的子类型。Fn 是 FnMut 与 FnOnce 的子类型。也就是说,需要 FnOnce 的地方可以用 FnMut,需要 FnMut 或 FnOnce 的地方可以用 Fn。

定义接收闭包的函数时,若可以(即只调用一次)应取 FnOnce,否则取 FnMut,最后才是 Fn。这给调用方最大的灵活性。

相反,当你自己有一个闭包时,最灵活的是 Fn(可以传给三种闭包 trait 中任何一种的消费者),然后是 FnMut,最后是 FnOnce。

编译器还会根据闭包捕获的内容推断 Copy(例如 add_suffix)与 Clone(例如 take_and_reverse)。函数指针(对 fn 项的引用)实现 Copy 与 Fn。

最后修改 August 11, 2026: 更新 (70a5af133)