[rust]闭包

31 阅读3分钟

定义

  1. 闭包(Closure)是可以保存进变量或者作为参数传递给其他函数的匿名函数
  2. 闭包和函数不同的是,闭包允许捕获调用者作用域中的值

基础使用

不带参数,不带返回值

fn main() {
    // 定义闭包
    let empty_closure = || {
        println!("this is a empty closure")
    };
    // 执行闭包
    empty_closure()
}

带参数,带返回值

fn main() {
    // 输入一个数加1后的结果
    let add_one_v1 = |x: u32| -> u32 {
        x + 1
    };
    let i = add_one_v1(2);
    println!("{}", i) // 3
}

简写:入参和出参编译器可以自动推导

fn main() {
    // 返回值类型可以省略
    let add_one_v2 = |x: u32| { x + 1 };
    // 入参类型可以省略
    let add_one_v3 = |x| { x + 1 };
    // 表达式括号也可以省略
    let add_one_v4 = |x| x + 1;

    println!("{}",add_one_v2(2)); 
    println!("{}",add_one_v3(2));
    println!("{}",add_one_v4(2));
}

编译器不能推导两次

fn main() {
    let return_x = |x| x;
    println!("{}", return_x("hello")); // x 已经被推导为 &str 类型了

    println!("{}", return_x(123)); // 不能再传入整型
}

带Fn trait的闭包

类似一个回调函数

// 实现一个缓存,只处理第一次传入的值并保存
struct Cache<T> {
    callback: T,
    value: Option<i32>,
}

impl<T> Cache<T>
where
    T: Fn(i32) -> i32, // 泛型约束为Fn trait
{
    // 传入一个闭包
    fn creat_new(callback: T) -> Cache<T> {
        Cache {
            callback, // 结构体同名属性,可简写
            value: None,
        }
    }

    fn get_value(&mut self, value: i32) -> i32 {
        match self.value {
            None => {
                let v = (self.callback)(value); // 获取闭包并调用
                self.value = Some(v); // self 必须是可变的这里才能赋值
                v
            }
            Some(val) => val
        }
    }
}

fn main() {
    let mut cache= Cache::creat_new(|x| x + 1);
    let v1 = cache.get_value(1);
    println!("{}", v1); // 2
    let v2 = cache.get_value(1);
    println!("{}", v2); // 2
}

捕获环境中的变量

闭包不仅可以作为一个函数执行,还可以访问和使用闭包定义时所在作用域中的变量。根据闭包捕获变量的方式不同,闭包的行为和类型会有所不同

闭包捕获变量的方式也对应函数三种获取参数的方式

闭包有三种捕获环境变量的方式,被编码为如下三个Fn trait

  1. 通过不可变借用Fn:当一个闭包只需要读取环境中的变量时使用
  2. 通过可变借用FnMut:当一个闭包需要修改环境中的变量时使用
  3. 通过转移所有权FnOnce:当一个闭包需要获取环境中变量的所有权时使用,且这种捕获方式要求闭包只能被调用一次,因为捕获的变量所有权被转移后不能再被使用

不可变借用

fn main() {
    let i = 5;
    let add_i = |x| x + i;
    println!("{}", add_i(5)) // 10
}

可变借用,环境变量和闭包都要是mut

fn main() {
    let mut i = 5;
    let mut modify_i = |x| i = x;
    modify_i(10);
    println!("{}", i) // 10
}

move关键字转移所有权

fn main() {
    let x = vec![1, 2, 3];
    let equal_val = move |z| { z == x };
    let y = vec![1, 2, 3];
    assert!(equal_val(y));
    // 再次使用时会报错
    println!("{:?}",x);
}