我们将在这一章学习更多高级功能
让我们来探索有关于函数和闭包的高级功能:函数指针以及返回值闭包
函数指针
除了向函数传递闭包之外,还可以传递函数,使用的是函数指针fn,函数的类型是fn(使用f)以免与Fn闭包trait相混淆。指定参数为函数指针的语法类似于闭包
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);
print!("The answer is: {}",answer)
}
不同于闭包,fn是一个类型而不是一个trait,所以直接指定fn作为参数而不是声明一个带有Fn作为trait bound的泛型参数
函数指针实现了所有三个闭包trait (Fn FnMut FnOnce),所以可以在调用期望闭包的函数时传递函数指针作为参数。倾向于编写使用泛型和闭包trait的函数,这样它就能接受函数或者闭包作为参数
一个只期望接受fn而不接受闭包的例子是与不存在闭包的外部代码交互时:C语言的函数可以接受函数作为参数,但C语言没有闭包
我们来看一个例子,它既可以使用内联函数定义的闭包又可以使用命名函数的例子
let list_of_numbers = vec![1,2,3];
let list_of_strings: Vec = list_of_numbers
.iter()
.map(|i| i.to_string())
.collect();
使用map函数将数字vector转换为字符串vector,就可以使用闭包
let list_of_numbers = vec![1,2,3];
let list_of_strings: Vec = list_of_numbers
.iter()
.map(ToString::to_string)
.collect();
或者可以将函数作为map的参数来代替闭包,如上
注意:这里必须使用完全限定语法,因为存在多个叫to_string的函数;这里使用了定义于ToString trait 的to_string函数,标准库为所有实现了Display的类型实现了这个trait
另一个实用的模式暴露了元组结构体和元组结构体枚举成员的实现细节
这些项使用()作为初始化语法,它们看起来象函数的调用,同时也确实被实现为返回由参数构造的实例的函数。它们也被称为实现了闭包trait的函数指针,并可以采用类似如下方式调用
enum Status {
Value(u32),
Stop,
}
let list_of_statuses: Vec =
(0u32..20)
.map(Status::Value)
.collect();
这里创建了Status::Value 实例,它通过map 用范围的每一个u32值调用Status::Value的初始化函数。大家可以根据喜好选择使用函数还是闭包,但它们会产生同样的代码
返回闭包
闭包表现为trait,只意味着不能直接返回它。对于大部分需要返回trait的情况,可以使用实现了期望返回的trait的具体类型来代替函数的返回值。但是这不能用于闭包,因为它没有一个可以返回的具体类型,例如不允许使用函数指针fn作为返回值的类型
下面的代码尝试返回闭包,但是会编译失败
fn returns_closure()->Fn(i32) -> i32 {
|x| x+1
}
error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static:
std::marker::Sized` is not satisfied
-->
|
1 | fn returns_closure() -> Fn(i32) -> i32 {
| ^^^^^^^^^^^^^^ `std::ops::Fn(i32) -> i32 + 'static`
does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for
`std::ops::Fn(i32) -> i32 + 'static`
= note: the return type of a function must have a statically known size
错误指向了sized trait!Rust并不知道需要多少空间来存储闭包,但我们知道这个问题可以用 trait对象解决
fn returns_closure()->Box i32> {
Box::new(|x| x+1)
}
现在代码可以编译了!