Rust-- closures 匿名函数

273 阅读3分钟

closure首先是个匿名函数,而且像Functional Programming中一样,也是一等公民,

rust中称之为closure, 其实表示它更重要的特点是可以从周围环境中捕获一些值.

先看看closure的基本写法

fn main() {
    // function,将此函数写成closure的形式
    fn add_one_v1(x: u32) -> u32 {x + 1}
    
    // closures
    let add_one_v2 = |x: u32| -> u32 {x + 1};
    let add_one_v3 = |x: u32|        {x + 1};
    let add_one_v4 = |x: u32|         x + 1;

    // 如果compiler能自动推断出类型,则无需明确写出类型
    
}


// 看看能自动推断出类型的例子:

fn main() {

    let example_closure = |x|x;

    // inferred as String, 编译器在example_closure第一次被调用时,推断出x为String类型
    let s = example_closure(String::from("hello"));

    println!("{}", s);

    let n = example_closure(5); // 编译报错,因为之前已经推断x为String类型

}

编译器提示:

error[E0308]: mismatched types
  --> src/main.rs:57:29
   |
57 |     let n = example_closure(5);
   |                             ^- help: try using a conversion method: `.to_string()`
   |                             |
   |                             expected struct `String`, found integer

看一个较为复杂的例子:

#[derive(Debug, PartialEq, Copy, Clone)]
enum ShirtColor {
    Red,
    Blue,
}

struct Inventory {
    shirts: Vec<ShirtColor>,
}

impl Inventory {
    fn giveaway(&self, user_preference: Option<ShirtColor>) -> ShirtColor {
        user_preference.unwrap_or_else(|| self.most_stocked()) // 此处用到了closure, 当无法unwrap user_preference时,执行此closure
    }

    fn most_stocked(&self) -> ShirtColor {
        let mut num_red = 0;
        let mut num_blue = 0;

        for color in &self.shirts {
            match color {
                ShirtColor::Red => num_red += 1,
                ShirtColor::Blue => num_blue += 1, 
            }
        }

        if num_red > num_blue {
            ShirtColor::Red
        } else {
            ShirtColor::Blue
        }
    }
}


fn main() {
    let store = Inventory {
        shirts: vec![ShirtColor::Blue, ShirtColor::Red, ShirtColor::Blue],
    };

    let user_pref1 = Some(ShirtColor::Red);
    let giveaway1 = store.giveaway(user_pref1);
    println!("The user with preference {:?} gets {:?}", user_pref1, giveaway1);

    let user_pref2 = None;
    let giveaway2 = store.giveaway(user_pref2);
    println!("The user with preference {:?} gets {:?}", user_pref2, giveaway2);
}


程序执行结果:

The user with preference Some(Red) gets Red
The user with preference None gets Blue

之前提到,closure是一种匿名函数,既然是函数,就会遇到rust中的ownership、borrow 问题

  • 首先讨论 immutable borrow的情形:
fn main() {

    let list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    // borrows list
    let only_borrows = || println!("From closure: {:?}", list);

    println!("Before calling closure: {:?}", list);
    only_borrows(); // 调用已定义的 closure
    println!("After calling closure: {:?}", list);
}

输出结果:
Before defining closure: [1, 2, 3]
Before calling closure: [1, 2, 3]
From closure: [1, 2, 3]
After calling closure: [1, 2, 3]

  • mutable borrow 的情形:
fn main() {

    let mut list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    // captures mutable reference to `list`
    let mut borrows_mutably = || list.push(7);

    println!("Before calling closure: {:?}", list); // 编译报错
    borrows_mutably(); // 调用已定义的 closure
    println!("After calling closure: {:?}", list);
}

编译器提示如下:

error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
  --> src/main.rs:56:46
   |
54 |     let mut borrows_mutably = || list.push(7);
   |                               -- ---- first borrow occurs due to use of `list` in closure
   |                               |
   |                               mutable borrow occurs here
55 | 
56 |     println!("Before calling closure: {:?}", list); // 编译报错
   |                                              ^^^^ immutable borrow occurs here
57 |     borrows_mutably(); // 调用已定义的 closure
   |     --------------- mutable borrow later used here
   
   
该原因就是之前学习borrow的三大规则时提到的,在mutable borrow没彻底结束前,无法使用 immutable borrow

所以解决方法就是,在mutable borrow之后,再使用 immutable borrow(println!属于这种borrow), 对应上例,即把 Before calling closure一行注释掉


这时的输出结果为:

Before defining closure: [1, 2, 3]
After calling closure: [1, 2, 3, 7]


  • take ownership 的情形:

在本例中,假设我们有两个线程, 一个main主线程,还有一个新创建的线程 由于我们不知道具体什么时候新创建好一个线程,也不知道哪个线程先结束运行(这一切都由操作系统来决定, 对于程序员来说是不确定的)

use std::thread;

fn main() {

    let list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    thread::spawn(move || println!("From thread: {:?}", list))
        .join()
        .unwrap();
    println!("{:?}", list); // 编译报错

   
}



编译器提示:

error[E0382]: borrow of moved value: `list`
  --> src/main.rs:58:22
   |
52 |     let list = vec![1, 2, 3];
   |         ---- move occurs because `list` has type `Vec<i32>`, which does not implement the `Copy` trait
...
55 |     thread::spawn(move || println!("From thread: {:?}", list))
   |                   ------- value moved into closure here ---- variable moved due to use in closure
...
58 |     println!("{:?}", list); // 编译报错
   |                      ^^^^ value borrowed here after move

高级部分,未完待续

实际上,每个closure都需要实现如下几个的trait:

FnOnce applies to closures that can be called once. All closures implement at least this trait, because all closures can be called. A closure that moves captured values out of its body will only implement FnOnce and none of the Other Fn traits, because it can only be called once.

FnMut applies to closures that don't move captured values out of their body, but that might mutate the captured values. These closures can be called more than once.

Fn applies to closures that don't move captured values out of their body and that don't mutate captured values, as well as closures that capture nothing from their environment. These closures can be called more than once without mutating their environment, which is important in cases such as calling a closure multiple times concurrently.

参考

[1] www.youtube.com/watch?v=IAe…