四、函数与闭包
Published on
函数
函数使用fn关键字声明,如有需要,则指定参数名称与类型,以及返回值的类型。返回方式除了可以使用return外,还可以在最后一条语句给出一个表达式。
fn add_one(x: i32) -> i32 {
x + 1
}
语句与表达式
- Rust程序由语句组成,常见的语句有let语句与表达式语句
- 代码块也是表达式,它的最后一个表达式被返回。需要注意的是,如果最后一个表达式以分号结尾,则返回值为()
函数指针
函数指针的类型是fn,在使用时还需要指定输入输出类型。
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
// 将add_one函数指针作为do_twice的参数
let answer = do_twice(add_one, 5);
函数指针fn是一个类型,所以在作为参数时可以直接使用,而无需使用泛型并使用闭包特性进行限制。此外,fn实现了全部3种闭包特性(Fn,FnMut与FnOnce),这意味着可以将函数指针传给所有将闭包作为参数的函数。因此,在写函数时最好使用泛型与闭包特性,这样函数与闭包都可以作为参数。
但是,在与外部代码交互时,可能只接收函数指针作为参数。
闭包
闭包是可以捕获环境中变量的函数,相当于lambda表达式。
// 一个捕获了变量x的闭包
|val| val + x
闭包的特点有:
- 使用||代替()
- 如果只有一条语句可以省略{}
- 可以捕获环境中的变量
捕获变量
闭包可以捕获环境中的变量。可以通过以下3种方式:
- 引用:&T
- 可变引用:&mut T
- 值:T
// 引用捕获
let color = String::from("green");
let print = || println!("`color`: {}", color);
print();
// 可变引用捕获
let mut count = 0;
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
inc();
// 值捕获
let movable = Box::new(3);
let consume = || {
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
consume();
// 使用move强制转移所有权
let haystack = vec![1, 2, 3];
let contains = move |needle| haystack.contains(needle);
println!("{}", contains(&1));
类型匿名性
每个闭包在定义时,编译器会为它声明一个匿名结构体类型以保存捕获的变量,并根据其行为为该类型实现以下3个特性之一:
- Fn:使用引用捕获
- FnMut:使用可变引用捕获
- FnOnce:使用值捕获
类似函数指针,也可以声明一个指定输入输出类型的闭包类型,如Fn(i32) -> i32,它实际上是一个更加具体的闭包特性。
作为输入参数
在将闭包作为函数的输入输出时,需要显式说明其类型,然而闭包的具体类型是无法获取的。此时可以将以上3个特性作为特性范围限制,编写泛型函数。如果知道一个闭包的具体输入输出类型,也可以使用具体的闭包特性作为参数。
// 将一个值捕获闭包作为参数
fn apply<F>(f: F) where
F: FnOnce() {
f();
}
// 将一个知道输入输出类型的闭包作为参数
fn apply_to_3<F>(f: F) -> i32 where
F: Fn(i32) -> i32 {
f(3)
}
作为输出参数
使用方法与作为输入参数类似,可以使用impl语法糖。需要注意闭包作为输出参数时必须使用move,因为捕获的引用变量会在函数返回后释放。
fn create_fn() -> impl Fn() {
let text = "Fn".to_owned();
move || println!("This is a: {}", text)
}
fn create_fnmut() -> impl FnMut() {
let text = "FnMut".to_owned();
move || println!("This is a: {}", text)
}
fn create_fnonce() -> impl FnOnce() {
let text = "FnOnce".to_owned();
move || println!("This is a: {}", text)
}
返回动态类型的闭包
上述方法声明的函数是静态泛型函数,实际上的输入输出类型是固定的。与普通特性类似,可以使用Box<dyn Fn>语法由一个非泛型函数返回位于堆上的不同类型的闭包。
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
高阶函数
使用其它函数(或闭包)作为参数的函数。
发散函数
!是一种特殊的永不返回类型,将其作为返回值的函数称为发散函数。永不返回类型与空类型()不同,它可以用在需要任何类型的地方。
fn foo() -> ! {
panic!("This call never returns.");
}