• Rust基础笔记:闭包


     语法

    Closure看上去是这样的:

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

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

    它也可以被写成这样:

        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中参数和返回值的类型都是可以省略的,下面这种形式也是可以的:

    let plus_one = |x| x + 1;

    闭包和它的环境

    一个小例子:

        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的作用域内,又发生了一次可变引用,所以就违反了所有权系统中的如下规则:

    如果对一个绑定进行了不可变引用,那么在该引用未超出作用域之前,不可以再进行可变引用,反之也是一样。

    对代码做出如下修改即可:

        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中闭包的概念并不好理解,因为牵扯到了太多所有权的概念,可以先把所有权弄懂了,闭包也就好理解了。

     
  • 相关阅读:
    安卓android.support.design使用中的问题
    处理requests SSl 证书问题
    python-excel
    post 请求包含相同参数
    关于zk 页面滚动问题 scroll
    Usefull Jquery
    Git 安装
    Comparison issues in TD
    Work Diary 12/13/17
    Unit10 I don't like work in the weekend
  • 原文地址:https://www.cnblogs.com/shiluoliming/p/11069220.html
Copyright © 2020-2023  润新知