Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6.2.3 闭包和所有权。能不能总结一个正交的规则 #164

Open
frostRed opened this issue Mar 9, 2019 · 5 comments
Open

6.2.3 闭包和所有权。能不能总结一个正交的规则 #164

frostRed opened this issue Mar 9, 2019 · 5 comments
Labels
第六章 第六章 精选 读者建议 来自读者对本书的建议
Milestone

Comments

@frostRed
Copy link

frostRed commented Mar 9, 2019

环境变量语义 环境变量操作 有无move 结果是哪种闭包 环境变量被怎么处理
复制语义 读环境变量 有 move Fn 闭包 &env
复制语义 读环境变量 无 move Fn闭包 &env
复制语义 修改环境变量 有 move
复制语义 修改环境变量 无 move
移动语义 读环境变量 有 move
移动语义 读环境变量 无 move
移动语义 修改环境变量 有 move FnMut 闭包 转移所有权
移动语义 修改环境变量 无 move FnMut 闭包 &mut env

现在书的内容看完之后有点懵,反而把我以前的理解搞乱了。

比如这段代码

fn main() {
    let mut s = 1;
    let c = || {
        println!("{:?}", s);
        println!("{:p}", &s);
    };
    c();
    c();
    let d = &mut s;
    println!("{:p}", d);
    println!("{:?}", s);
    println!("{:p}", &s);
}

按 179 页第二段说,闭包会以「不可变借用捕获了环境中的自由变量」,那不应该还能在闭包外面使用 &mut 的。

希望作者能帮我解惑。

@ZhangHanDong ZhangHanDong added the 第六章 第六章 label Mar 9, 2019
@ZhangHanDong ZhangHanDong added this to the 第二版 milestone Mar 9, 2019
@ZhangHanDong ZhangHanDong added the 读者建议 来自读者对本书的建议 label Mar 9, 2019
@ZhangHanDong
Copy link
Owner

ZhangHanDong commented Mar 9, 2019

@frostRed 感谢反馈,后续在勘误表里尝试总结一下。不过也希望你可以自己总结一下,分享出来。

@yim7
Copy link

yim7 commented Apr 14, 2019

在 frostRed 的表格上进行了补全,大家看看没有错误

环境变量语义 环境变量操作 有无 move 闭包 Trait 环境变量被怎么处理 闭包能否Copy
复制语义 读环境变量 有 move Fn copy
复制语义 读环境变量 无 move Fn immutable borrow
复制语义 修改环境变量 有 move FnMut copy
复制语义 修改环境变量 无 move FnMut mutable borrow ×
移动语义 读环境变量 有 move Fn move ×
移动语义 读环境变量 无 move Fn immutable borrow
移动语义 修改环境变量 有 move FnMut move ×
移动语义 修改环境变量 无 move FnMut mutable borrow ×
移动语义 消耗环境变量 有 move FnOnce move ×
移动语义 消耗环境变量 无 move FnOnce move ×

闭包 Trait

  1. 没有捕获环境变量以及只是不可变借用环境变量(捕获不可变引用的值)的闭包自动实现 Fn Trait
  2. 可变借用环境变量的闭包自动实现 FnMut Trait
  3. 消耗环境变量的闭包自动实现 FnOnce Trait
  4. 因为 FnOnce 是 FnMut 的父 Trait,FnMut又是Fn的父Trait,所以实现某个Trait时也会自动实现其父 Trait

move 作用

强行让闭包获取捕获的环境变量的所有权,这可能改变闭包本身的性质。下面例子就是,如果没有 move,闭包捕获的是一个 Copy 语意的不可变引用,所以闭包本身也会自动实现 Copy。在闭包前加上 move 强行转移所有权,导致闭包不能自动实现 Copy。

fn main() {
    let s = "hello".to_owned();
    let f = move || {
        println!("{}", s);
    };

    f(); 
    // foo(f);  // 因为 string move 到闭包环境里,让闭包不能自动实现 Copy
}

fn foo<F: Fn() + Copy>(f: F) {
    f()
}

同样的道理,用 move 可以让本来不能 Copy 的闭包自动实现 Copy (可变引用没有实现 Copy)

fn main() {
    let mut x = 0;
    let mut f = move || {
        x += 1;
        println!("{}", x);
    };
    
    f();
    foo(f);
    f();
    foo(f);
    
    println!("{}", x);
}

fn foo<F: FnMut() + Copy>(mut f: F) {
    f()
}

运行结果,作为函数参数传递的闭包每次复制新的闭包环境:

Standard Error
   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.85s
     Running `target/debug/playground`
Standard Output
1
2
2
3
0

闭包能否自动实现 Copy

取决于捕获的变量类型,如果捕获的是不可变引用、 move 的值本身实现了 Copy,闭包就能自动实现 Copy。

@ZhangHanDong
Copy link
Owner

@yim7 辛苦了,已加精。

@loxp
Copy link

loxp commented Aug 16, 2019

复制语义 | 读环境变量 | 无 move | Fn | copy | √

这里环境变量的处理方式不是copy, 而是immutable borrow.

fn main() {
    let mut a = 1;
    let print = || {&a;};
    let aa = &mut a;
    print();
}
error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable
 --> main.rs:4:14
  |
3 |     let print = || {&a;};
  |                 --   - first borrow occurs due to use of `a` in closure
  |                 |
  |                 immutable borrow occurs here
4 |     let aa = &mut a;
  |              ^^^^^^ mutable borrow occurs here
5 |     print();
  |     ----- immutable borrow later used here

error: aborting due to previous error

闭包的类型与闭包中使用环境变量的方式, 以及环境变量是否Copy有关;
闭包中环境变量的捕获方式, 与闭包中环境变量的使用方式, 以及是否有move有关; 至于是复制还是移动, 则与环境变量类型本身的语义有关.

可以确认一下是不是这样.

@yim7
Copy link

yim7 commented Aug 16, 2019

感谢纠正疏漏之处@loxp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
第六章 第六章 精选 读者建议 来自读者对本书的建议
Projects
None yet
Development

No branches or pull requests

4 participants