• 作为输入参量
    • 参见:

    作为输入参量

    虽然 Rust 在捕获临时变量的方式大多选择不带标注,但在编写函数时,这种不确定性是不允许的。当以闭包作为输入参数时,闭包的完整类型必须使用以下的其中一种 trait 来标注。它们的受限程度依次递减,依次是(原文:In order of decreasing restriction, they are):

    • Fn:闭包需要通过引用(&T)捕获
    • FnMut:闭包需要通过可变引用(&mut T)捕获
    • FnOnce:闭包需要通过值(T)捕获

    在值传值(variable-by-variable)的基础上,编译器将以限制最少的方式来捕获变量。

    例如考虑一个标注为 FnOnce 的参量。这意味着闭包可能通过 &T&mut TT 来捕获,但是编译器将根据所捕获变量在闭包的使用情况做出最终选择。

    这是因为若移动语义(move)可能的话,则任意借用类型也应该是可行的。注意反过来就不再成立:如果参量是 Fn,那么通过 &mut TT 捕获的情况就不允许了。

    在下面的例子中,试着换换 FnFnMutFnOnce 的使用,看看会发生什么:

    1. // 将闭包作为参数并调用它的函数。
    2. fn apply<F>(f: F) where
    3. // 闭包没有输入值和返回值。
    4. F: FnOnce() {
    5. // ^ 试一试:将 `FnOnce` 换成 `Fn` 或 `FnMut`。
    6. f();
    7. }
    8. // 使用闭包并返回一个 `i32` 整型的函数。
    9. fn apply_to_3<F>(f: F) -> i32 where
    10. // 闭包处理一个 `i32` 整型并返回一个 `i32` 整型。
    11. F: Fn(i32) -> i32 {
    12. f(3)
    13. }
    14. fn main() {
    15. use std::mem;
    16. let greeting = "hello";
    17. // 不可复制的类型。
    18. // `to_owned` 从借用的数据创建属于自己的数据。
    19. let mut farewell = "goodbye".to_owned();
    20. // 捕获 2 个变量:通过引用方式的 `greeting` 和
    21. // 通过值方式的 `farewell`。
    22. let diary = || {
    23. // `greeting` 使用引用方式:需要 `Fn`。
    24. println!("I said {}.", greeting);
    25. // 改变迫使 `farewell` 变成了通过可变引用来捕获。
    26. // (原文:Mutation forces `farewell` to be
    27. // captured by mutable reference.)
    28. // 现在需要 `FnMut`。
    29. farewell.push_str("!!!");
    30. println!("Then I screamed {}.", farewell);
    31. println!("Now I can sleep. zzzzz");
    32. // 手动调用 drop 将 `farewell` 强制转成通过值来捕获。
    33. // (原文:Manually calling drop forces `farewell` to
    34. // be captured by value. Now requires `FnOnce`.)
    35. // 现在需要 `FnOnce`。
    36. mem::drop(farewell);
    37. };
    38. // 调用处理闭包的函数(原文:Call the function
    39. // which applies the closure)。
    40. apply(diary);
    41. // `double` 满足 `apply_to_3` 的 trait 限定。
    42. let double = |x| 2 * x;
    43. println!("3 doubled: {}", apply_to_3(double));
    44. }

    参见:

    std::mem::drop, Fn, FnMut, 和 FnOnce