13.2.0. 写在正文之前
Rust语言在设计过程中收到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。
在本章中,我们会讨论 Rust 的一些特性,这些特性与许多语言中通常称为函数式的特性相似:
- 闭包(本文)
- 迭代器
- 使用闭包和迭代器改进I/O项目
- 闭包和迭代器的性能
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
13.2.1. 闭包的类型推断
和fn
定义的函数不同,闭包不强制要求标注参数和返回值的类型。
函数需要强制标注是因为它是暴露给用户的显示接口的一部分,严格定义接口有助于所有人对参数和返回值的类型取得共识。
闭包并不会被用于这样的暴露接口,只会被存于变量中,使用时也不需要命名,更不会被暴露给我们代码库的用户。所以,闭包不强制要求标注参数和返回值的类型。
而且闭包通常很短小,只在狭小的上下文中工作,编译器通常能推断出类型。当然你手动标注出来也不是不可以。
看个例子:
这是使用函数定义的代码:
fn simulated_expensive_calculation(intensity: u32) -> u32 {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
intensity
}
这是使用闭包的代码:
let expensive_closure = |num:u32| -> u32 {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
这里使用显式标注是因为没有前后文供Rust推断类型,如果有,就不需要:
fn generate_workout(intensity: u32, random_number: u32) {
let expensive_closure = |num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
if intensity < 25 {
println!("Today, do {} pushups!", expensive_closure(intensity));
println!("Next, do {} situps!", expensive_closure(intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!("Today, run for {} minutes!", expensive_closure(intensity));
}
}
}
这里的参数num
不需要显式声明类型是因为下文的调用中传进去的参数intensity
的类型为u32
,Rust推断出num
的类型为u32
。
13.2.2. 函数和闭包定义的语法
这里有4个例子:
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
- 第一个是函数的定义,有函数名,形参名及类型和返回值类型
- 第二个是闭包的定义,有参数和返回值的类型。这个闭包看着和函数的定义差不多。
- 第三个同样是闭包,但是没有标注参数和返回值的类型,就得靠编译器推断了。
- 第四个闭包跟第三个的不同之处在于没有了花括号
{}
。因为只有一个表达式,所以闭包的{}
也可以被省略
13.2.3. 闭包的类型推断
闭包的定义最终只会为参数/返回值推断出唯一具体的类型。
看个例子:
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);
输出:
$ cargo run
Compiling closure-example v0.1.0 (file:///projects/closure-example)
error[E0308]: mismatched types
--> src/main.rs:5:29
|
5 | let n = example_closure(5);
| --------------- ^- help: try using a conversion method: `.to_string()`
| | |
| | expected `String`, found integer
| arguments to this function are incorrect
|
note: expected because the closure was earlier called with an argument of type `String`
--> src/main.rs:4:29
|
4 | let s = example_closure(String::from("hello"));
| --------------- ^^^^^^^^^^^^^^^^^^^^^ expected because this argument is of type `String`
| |
| in this closure call
note: closure parameter defined here
--> src/main.rs:2:28
|
2 | let example_closure = |x| x;
| ^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `closure-example` (bin "closure-example") due to 1 previous error
Rust编译器在闭包第一次被调用时发现它接收的值和输出的值都是String
类型,就锁定这个闭包的参数和返回值都是String
类型。所以后面又使用i32
类型时就会报错。