5分钟速读之Rust权威指南(二十一)闭包

闭包

闭包这个名字在前端学的时候比较晕,因为闭包rust中的闭包与JS的闭包完全不是一个东西,rust中的闭包是一种可以存入变量或作为参数传递给其他函数的匿名函数。

定义闭包

像定义变量一样定义闭包,想象一下TS中的箭头函数,区别只是把包裹参数的圆括号换成了两条竖线:

let add = |a: u32, b: u32| -> u32 {
  a + b
};
add(1, 2); // 3
复制代码

闭包并不强制要求标注参数和返回值的类型,编译器会自行推断:

let add = |a, b| {
  a + b
};
复制代码

如果闭包体内只有一个表达式,可以更加简写:

let add = |a, b| a + b;
复制代码

函数不支持对函数体以外的变量进行引用,而闭包中允许对外部变量进行引用:

let a = 1;
let b = 2;
let add = || {
  a + b
};
add(); // 3
复制代码

函数不支持引用外部变量:

let a = 1;
let b = 2;
fn add() -> i32 {
  a + b // error,找不到变量a,b
};
add();
复制代码

在结构体中使用闭包

在结构体中的闭包需要声明trait约束:

// 声明闭包的约束为:Fn(u32) -> u32
struct Cacher<T: Fn(u32) -> u32> {
  calculation: T,
  value: Option<u32>
}

impl<T> Cacher<T: Fn(u32) -> u32> {
  fn new(calculation: T) -> Cacher<T> {
    Self {
      calculation,
      value: Option::None
    }
  }
	
  fn value(&mut self, arg: u32) -> u32 {
    match self.value {
      Option::Some(v) => v,
      Option::None => {
        // 调用闭包,为了区分自身的方法,需要加括号
        (self.calculation)(2);
        self.value = Option::Some(arg);
        arg
      }
    }
  }
}
复制代码

闭包的trait约束种类

闭包有三种类型,对应对于外界变量的引用方式:

  • FnOnce将引用的外界变量所有权移动进闭包。
  • Fn可以从环境中不可变地借用值。
  • FnMut可以从环境中可变地借用值并对它们进行修改。

利用move关键字强制将引用的外部变量的所有权转移到闭包中

let a = String::from("1");
let b = String::from("1");
let cmp = move|| a == b;

println!("{}", cmp());
println!("{}", a); // error,变量被移动了
println!("{}", b); // error,变量被移动了
复制代码

笔者在学习的过程中有看过这样一篇介绍生命周期和闭包的相关文章,感兴趣的话可以去阅读:闭包与所有权

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享