Rust基础笔记:闭包

语法

Closure看上去是这样的:html

let plus_one = |x: i32| x + 1;
    assert_eq!(2, plus_one(1));

首先建立一个绑定plus_one,而后将它分配给一个closure,body是一个expression,注意{ } 也是一个expression。express

它也能够被写成这样:闭包

let plus_two = |x| {
        let mut result: i32 = x;
    
        result += 1;
        result += 1;
    
        result
    };
    assert_eq!(4, plus_two(2));

和常规的函数定义相比,区别就是closure没有使用关键词 fn ,区分一下:函数

fn  plus_one_v1   (x: i32) -> i32 { x + 1 }
let plus_one_v2 = |x: i32| -> i32 { x + 1 };
let plus_one_v3 = |x: i32|          x + 1  ;

值得注意的是在closure中参数和返回值的类型都是能够省略的,下面这种形式也是能够的:code

let plus_one = |x| x + 1;

闭包和它的环境

一个小例子:htm

let num = 5;
    let plus_num = |x: i32| x + num;
    
    assert_eq!(10, plus_num(5));

也就是说,plus_num引用了一个在它做用于中的变量num,具体地说这是一个borrow,它知足全部权系统的要求,来看一个错误的例子:资源

let mut num = 5;
let plus_num = |x: i32| x + num;

let y = &mut num;

error: cannot borrow `num` as mutable because it is also borrowed as immutable
    let y = &mut num;
                 ^~~

在上面的代码中,plus_num已经对num作了不可变引用,而在plus_one的做用域内,又发生了一次可变引用,因此就违反了全部权系统中的以下规则:作用域

若是对一个绑定进行了不可变引用,那么在该引用未超出做用域以前,不能够再进行可变引用,反之也是同样。get

对代码作出以下修改便可:编译器

let mut num = 5;
    {
        let plus_num = |x: i32| x + num;
    
    } // plus_num goes out of scope, borrow of num ends
    
    let y = &mut num;

再看一个例子:

let nums = vec![1, 2, 3];
    let takes_nums = || nums;
    println!("{:?}", nums);

有问题吗?
有,并且是大问题,编译器的报错以下:

closure.rs:8:19: 8:23 error: use of moved value: `nums` [E0382]
closure.rs:8    println!("{:?}", nums);

从错误中能够看出来,在最后一个输出语句中,nums已经没有对资源 vec![1, 2, 3] 的 全部权了,该资源的全部权已经被move到了closure中去了。

那么问题来了:

为何在前面的例子中closure是borrow,而到了这里就变成了move了呢?

咱们从头梳理一遍:

let mut num = 5;
    let plus_num = || num + 1;
    let num2 = &mut num;
Error:
closure.rs:5:21: 5:24 error: cannot borrow `num` as mutable because it is also borrowed as immutable
closure.rs:5     let num2 = &mut num;

说明在closure中发生了immutable borrow,这样才会和下面的&mut冲突,如今咱们来作一个改动:

let plus_num = || num + 1; 
    // 改为以下语句
    let mut plue_num = || num += 1;

再编译一次:

Error:
closure.rs:4:17: 4:20 error: cannot borrow `num` as mutable more than once at a time
closure.rs:4 let num2 = &mut num;

能够发现,在closure中发生了mutable borrow,为何会这样呢?

在closure无非就是这3种状况:

  • by reference: &T

  • by mutable reference: &mut T

  • by value: T

    至因而这3个中的哪个,取决于你closure内部怎么用,而后编译器自动推断绑定的类型是Fn() FnMut() 仍是FnOnce()

let plus_num = || num + 1;         // 这个只须要引用便可,因此plus_num类型为Fn()
    let mut plue_num = || num += 1;    // 这个则须要&mut T,因此plus_num类型为FnMut()
    // 这是手册里的一个例子
    // 这是一个没有实现Copy trait的类型
    let movable = Box::new(3);
    // `drop` 须要类型T,因此closure环境就须要 by value T.,因此consume类型为FnOnce()
    let consume = || {
        drop(movable);    // 这里发生了move
    };
    // 因此这个consume只能执行一次
    consume();

有一点要注意的是:
在前面的例子应该分红两类:

  1. let a= 100i32;

  2. let a = vec![1,2,3];

区别就是i32类型实现了copy trait,而vector没有!!!

参考:http://rustbyexample.com/fn/closures/capture.html

Move closure

使用move关键字,强制closure得到全部权,但下面的例子得注意一下:

let num = 5;
    let owns_num = move |x: i32| x + num;

尽管这里使用move,变量遵循move语义,可是,在这里5实现了Copy,因此owns_own得到的是 5 的拷贝的全部权,有什么区别呢?
来看看这段代码:

let mut num = 5;
    {
        let mut add_num = |x: i32| num += x;
        add_num(5);
    }
    assert_eq!(10, num);

这段代码获得的是咱们想要的结果,可是若是咱们加上move关键字呢?上面的代码就会报错,由于num的值还是 5 ,并无发生改变,

为何呢?
上面说到了,move强制闭包环境得到全部权,可是 5 实现了Copy,因此闭包得到的是其拷贝的全部权,同理闭包中修改的也是 5 的拷贝。

总结

在Rust中闭包的概念并很差理解,由于牵扯到了太多全部权的概念,能够先把全部权弄懂了,闭包也就好理解了。

相关文章
相关标签/搜索