本小节来学习Rust中的函数和闭包。
函数
Rust是函数式编程语言,函数是一等公民。函数本身可以作为参数进行传递,也可用作函数返回值的类型。
Rust中定义函数的关键字为fn
,函数体中最后一个表达式的结果即为函数的返回值。
函数作为变量使用
fn add(x: i32, y: i32) -> i32 {
x + y
}
fn sub(x: i32, y: i32) -> i32 {
x - y
}
// 函数可以被作为变量对待
let var_add: fn(i32, i32)->i32 = add;
// 取函数的引用
let ref_add = &add;
println!("{}", add(2,3));//5
println!("{}", var_add(2,3));//5
println!("{}", ref_add(2,3));//5
函数作为参数进行传递
代码中binary_op
函数的第一个参数是fn(i32,i32)->i32
类型的函数。
fn binary_op(op: fn(i32,i32)->i32, operand1: i32, operand2: i32) -> i32 {
op(operand1, operand2)
}
// add函数作为binary_op的第一个参数
println!("{}", binary_op(add, 2, 3));//5
函数类型作为返回值
fn get_binary_fn(t: u8) -> fn(i32,i32)->i32 {
match t {
b'-'=> sub,
_ => add,
}
}
let f = get_binary_fn(b'-');
println!("sub: {}", f(100,15));//85
如果函数中所有返回路径都是panic!,返回值类型可以写一个英文感叹号:
fn get_panic(x:i32) -> ! {
panic!("getPanic(): {}", "~~~");
}
get_panic(100);
闭包
闭包也是函数式编程中的一个概念。
官方文档中,闭包是这样定义的:
A closure expression produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured variables.
翻译过来就是:
闭包是一种匿名类型,一旦声明,就会产生一个新的类型,但这个类型无法被其它地方使用。这个类型就像一个结构体,会包含所有捕获的变量。
可以将rust闭包简单理解为编译后会存在于可执行文件代码段中的一个结构。结构中包含一个可以调用的函数,以及可能被需要的,从声明闭包的上下文中捕获的额外数据。
rust中闭包的书写形式为:|args| {codes}
- 可以没有输入参数;
- 如果codes中只有一个表达式,大括号可以省略;
- 如果在闭包前面加
move
关键字,则捕获的变量所有权将被转移到闭包内。
let print = ||{ println!("Hello") };
let add = |x: i32, y: i32|{ x+y };
闭包表现形式上像一个函数,用法上可以当做一类特殊的函数。
let closure1 = |x:i32,y:i32|x+y;
println!("{}", closure1(10,20)); //30
Rust中有FnOnce
、FnMut
、Fn
共3种适用于不同场景的闭包类型。
FnOnce闭包
标准库中FnOnce trait
的定义为:
pub trait FnOnce<Args> {
type Output;//返回值类型
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
call_once
函数第一个参数为self
,也就是闭包自身。即是说,FnOnce
闭包一旦被调用,闭包自身(看成一个结构体对象)的所有权被move到call_once函数内部。因此,外部可以调用FnOnce
类型的闭包至多一次。
//FnOnce闭包只能被外部调用一次
let name = String::from("rust");
// 这个闭包啥也不干,只是把输入greeting和从上下文捕获的参数name返回出去
let c = move |greeting: String| (greeting, name);
let result = c("hello~".to_string());
println!("result: {:?}", result);
// 无法再次调用,编译报错:use of moved value: `c`
let result = c("hi~".to_string());
FnMut闭包
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
FnMut闭中可以修改捕获的变量。
注意,FnMut trait
“继承”了FnOnce trait
,因此可以被当做FnOnce使用。call_mut()
方法中传入的是引用&mut self
,因此call_mut()
可以被调用多次。
但是别忘了,如果将FnMut
当成FnOnce
来使用,一旦调用了一次call_once()
方法,就不能再调用其他方法了。
let mut name = String::from("hello");
// 捕获 &mut name,然后可以修改name
let mut c_mut = || {
name.push_str(" rust");
println!("c: {}", name);
};
c_mut();// c: hello rust
Fn
pub trait Fn<Args>: FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
Fn trait
“继承”了 FnMut trait
,或者说FnMut 是 Fn 的 super trait。这也就意味着任何需要 FnOnce 或者 FnMut 的场合,都可以传入满足 Fn 的闭包。
如果只是调用Fn
中的call()
方法,这个call()
可以被调用多次。
同样地,一旦调用了Fn
中的call_once()
,该闭包本身的所有权会被move走,就再也不能调用其他方法了。