Closure traits

闭包或 lambda 表达式具有无法命名的类型。不过,它们会 实现特殊的 FnFnMutFnOnce 特征:

The special types fn(..) -> T refer to function pointers - either the address of a function, or a closure that captures nothing.

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: ");
}
This slide should take about 10 minutes.

An Fn (e.g. add_suffix) neither consumes nor mutates captured values. It can be called needing only a shared reference to the closure, which means the closure can be executed repeatedly and even concurrently.

An FnMut (e.g. accumulate) might mutate captured values. The closure object is accessed via exclusive reference, so it can be called repeatedly but not concurrently.

If you have an FnOnce (e.g. take_and_reverse), you may only call it once. Doing so consumes the closure and any values captured by move.

FnMutFnOnce 的子类型。FnFnMutFnOnce 的子类型。也就是说,您可以在任何 需要调用 FnOnce 的地方使用 FnMut,还可在任何需要调用 FnMutFnOnce 的地方 使用 Fn

When you define a function that takes a closure, you should take FnOnce if you can (i.e. you call it once), or FnMut else, and last Fn. This allows the most flexibility for the caller.

In contrast, when you have a closure, the most flexible you can have is Fn (which can be passed to a consumer of any of the 3 closure traits), then FnMut, and lastly FnOnce.

The compiler also infers Copy (e.g. for add_suffix) and Clone (e.g. take_and_reverse), depending on what the closure captures. Function pointers (references to fn items) implement Copy and Fn.