Rust-高级特征:函数指针与闭包

函数指针

我们之前学习过向函数传递闭包;也可以向函数传递常规函数。这在我们希望传递已经定义的函数而不是重新定义闭包作为参数时很有用。通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 fn (使用小写的"f") 以免与 Fn 闭包trait相混淆。fn 被称为 函数指针(function pointer)。指定参数为函数指针的语法类似于闭包,如示例1所示:

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

fn add_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}
  //调用 let answer
= add_twice(add_one, 5); println!("The answer is {}", answer);

示例1:使用fn类型接受函数指针作为参数

这会打印出 The anser is 12。do_twice中的 f 被指定为一个接受一个i32参数并返回i32fn。接着就可以在do_twice函数体中调用f。在main中,可以将函数名add_one作为第一个参数传递给do_twice。 

不同于闭包,fn是一个类型而不是一个trait,所以直接指定fn作为参数而不是声明一个带有Fn作为trait bound的泛型参数。

函数指针实现了所有三个闭包的trait (Fn、FnMut和FnOnce),所以总是可以在调用期望闭包的函数时传递函数指针作为参数。倾向于编写使用泛型和闭包trait的函数,这样它就能接受函数或闭包作为参数。

作为一个既可以使用内联定义的闭包又可以使用命名函数的例子,让我们看看一个map的应用。使用map函数将一个数字vector转换为一个字符串vector,就可以使用闭包,比如这样:

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

或者可以将函数作为map的参数来代替闭包,像这样:

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

返回闭包

闭包表现为trait,这意味首不能直接返回闭包。对于大部分需要返回trait的情况,可以使用实现了期望返回trait的具体类型来替代函数的返回值。但是这不能用于闭包,因为他们没有一个可返回的具体类型;例如不允许使用函数指针fn作为返回值类型。

以下代码尝试直接返回闭包,它并不能编译:

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

编译器给出的错误是:

error[E0746]: return type cannot have an unboxed trait object
   --> src/main.rs:144:24
    |
144 | fn return_closure() -> Fn(i32) -> i32 {
    |                        ^^^^^^^^^^^^^^ doesn't have a size known at compile-time

错误又一次指向了Sized trait! Rust并不知道需要多少空间来储存闭包。不过我们在之前了解到这种情况的解决方法:可以使用trait对象:

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

这段代码就可以通过编译。

深入学习

原文地址:https://www.cnblogs.com/johnnyzhao/p/15390444.html