Rust 08:函数和闭包(FnOnce、FnMut、Fn)


本小节来学习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中有FnOnceFnMutFn共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走,就再也不能调用其他方法了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值