高级函数和闭包

本节探讨了一些与函数和闭包相关的高级特性。 包括函数指针和返回闭包。

函数指针

我们已经讨论了如何将闭包传递给函数;您也可以将常规 功能对功能!当您想要传递 函数,而不是定义新的闭包。功能 coerce 转换为类型(带有小写 f),不要与 closure trait 混淆。该类型称为函数指针。传递函数 with 函数指针将允许您将函数用作其他 功能。fnFnfn

指定参数为函数指针的语法类似于 闭包的 Closure 表达式,如示例 19-27 所示,其中我们定义了一个函数,该函数将 1 添加到其参数中。该函数需要两个 parameters:指向任何采用参数的函数的函数指针 并返回一个 和 1 个值。该函数调用 function 两次,向其传递值,然后添加两个函数调用 结果一起。该函数使用参数 和 .add_onedo_twicei32i32i32do_twicefargmaindo_twiceadd_one5

文件名: src/main.rs

fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);

    println!("The answer is: {answer}");
}

示例 19-27:使用 fn 类型接受函数 pointer 作为参数

此代码打印 .我们指定 in 中的参数是一个 an,它接受一个 type 为 的参数并返回一个 .然后,我们可以调用 的主体。在 中,我们可以传递 函数名称作为 的第一个参数。The answer is: 12fdo_twicefni32i32fdo_twicemainadd_onedo_twice

与闭包不同,它是一个类型而不是一个 trait,因此我们将 parameter 类型,而不是使用 作为 trait bound 的 trait。fnfnFn

函数指针实现了所有三个闭包特征(、 、 和 ),这意味着您始终可以将函数指针作为 需要闭包的函数。最好使用泛型 type 和其中一个闭包 trait 的 set,以便你的函数可以接受 函数或闭包。FnFnMutFnOnce

也就是说,一个例子说明你只想接受而不是 闭包是指与没有闭包的外部代码交互时: C 函数可以接受函数作为参数,但 C 没有闭包。fn

举个例子,说明你可以使用内联定义的闭包或命名的 函数,让我们看看 trait 在 standard 库中提供的方法的用法。要使用该函数将 numbers 转换为字符串向量,我们可以使用闭包,如下所示:mapIteratormap

fn main() {
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(|i| i.to_string()).collect();
}

或者我们可以将函数命名为 argument to 而不是闭包, 喜欢这个:map

fn main() {
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(ToString::to_string).collect();
}

请注意,我们必须使用我们之前讨论的完全限定语法 在 “Advanced Traits” 部分中,因为 有多个名为 的函数可用。在这里,我们使用了 trait 中定义的函数,该函数的 库已为实现 .to_stringto_stringToStringDisplay

回想一下本章的“枚举值”部分 6 中,我们定义的每个枚举变体的名称也成为初始化器 功能。我们可以将这些初始化器函数用作函数指针,这些函数 实现 Closure trait,这意味着我们可以指定初始化器 函数作为接受闭包的方法的参数,如下所示:

fn main() {
    enum Status {
        Value(u32),
        Stop,
    }

    let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
}

在这里,我们使用 Range 中的每个值创建实例 ,该函数通过使用 的 初始化器函数 来调用 。 有些人喜欢这种风格,有些人喜欢使用闭包。他们 编译为相同的代码,因此请使用您更清楚的样式。Status::Valueu32mapStatus::Value

返回闭包

闭包由 trait 表示,这意味着您不能返回闭包 径直。在大多数情况下,您可能希望返回特征,您可以改为返回 trait 使用实现 trait 的具体类型作为 功能。但是,你不能用 Closure 来做到这一点,因为它们没有 可返回的 concrete 类型;不允许使用该功能 pointer 作为返回类型。fn

以下代码尝试直接返回一个闭包,但它不会编译:

fn returns_closure() -> dyn Fn(i32) -> i32 {
    |x| x + 1
}

编译器错误如下:

$ cargo build
   Compiling functions-example v0.1.0 (file:///projects/functions-example)
error[E0746]: return type cannot have an unboxed trait object
 --> src/lib.rs:1:25
  |
1 | fn returns_closure() -> dyn Fn(i32) -> i32 {
  |                         ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
help: consider returning an `impl Trait` instead of a `dyn Trait`
  |
1 | fn returns_closure() -> impl Fn(i32) -> i32 {
  |                         ~~~~
help: alternatively, box the return type, and wrap all of the returned values in `Box::new`
  |
1 ~ fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
2 ~     Box::new(|x| x + 1)
  |

For more information about this error, try `rustc --explain E0746`.
error: could not compile `functions-example` (lib) due to 1 previous error

错误再次引用了 trait!Rust 不知道有多少空间 它需要存储 closure。我们之前看到了这个问题的解决方案。 我们可以使用 trait 对象:Sized

fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

这段代码编译得很好。有关 trait 对象的更多信息,请参阅 部分“使用允许不同 类型”。

接下来,让我们看看宏!

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准