1. 程式人生 > >Rust : 閉包、move 與自由變數的穿越

Rust : 閉包、move 與自由變數的穿越

閉包對進入其中的自由變數而言,有點象黑洞。自由變數進去了,很難再逃脫了。除非,有特別的力量。move,你該上場了,開始你的表演…….

一、copy trait 下move

我們知道,象i32,i64,等實現了copy trait。在賦值等行為是會自動copy一份。

move在閉包中的作用是,可以強制獲取環境變數的所有權:

情景1 :有move, 作用域相同

    let mut num1 = 5;
    let mut f1 = move |x: i32| num1 = x + num1;
    let data1 = f1(2_i32);
    println!("num1:{:?} data1:{:?}"
, num1, data1);

結論:有move下,自由變數可以穿越閉包函式。

情景2:無move,作用域相同

    let mut num2 = 5;
    let mut f2 = |x: i32| num2 = x + num2;
    let data2 = f2(2_i32);
    //println!("num2:{:?} data2:{:?}", num2, data2);=>error!, num2已經被借走了

結論:無move下,自由變數是無法穿越閉包函式。

情景3:無move,作用域不同

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

情景4:有move,作用域不同

如果我們換成一個 move 閉包,就會出現不同:

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

我們只得到 5。而不是從 num 得到可變的 borrow 我們對副本擁有所有權。

總結一下:

在實現了copy trait的自由變數,在move的幫助下,可以實現對閉包的穿越。

二、no copy trait下的move

對於沒有實現copy trait的String型別的環境變數,情況會如何?

1、有move, 作用域相同

    let mut str2 = "julia book".to_string();
    let str2_0 = "i love it";
    let mut f4 = move |x: &str| str2 = str2 + x + str2_0;
    let _str2 = f4(" 2013");
    println!("str2_0:{:?}", str2_0);//無影響
    //println!("str2:{:?}", str2); //=> error: str2也已經被借用了

2、無move,作用域相同

    let mut str2 = "julia book".to_string();
    let str2_0 = "i love it";
    let mut f4 = |x: &str| str2 = str2 + x + str2_0;
    let _str2 = f4(" 2013");
    println!("str2_0:{:?}", str2_0); //不變化
    //println!("str2:{:?}", str2); //=> error: str2也已經被借用了

以上1、2兩種情況,有、無move情況相同,對不可變自由變理無影響,但對可變變數有影響;

3、有move,作用域不相同

    let mut mybook = "rust".to_string();
    let comment: &str = "ok?";
    {
        let f = move |x: &str| {
            mybook = mybook + x + comment;
            println!("move=> mybook:{:?}", mybook);
        };
        f("primer is a good book!");
    };
    println!("comment:{:?}", comment);//=> comment仍可用
    //println!("mybook:{:?}", mybook); //=> 報錯,mybook已經被f move走了,不存在

4、 無move,作用域不相同

    let mut mybook = "rust".to_string();
    let comment: &str = "ok?";
    {
        let f = |x: &str| {
            mybook = mybook + x + comment;
            println!("無move=> mybook:{:?}", mybook);
        };
        f("primer is a good book!");
    };
    println!("comment:{:?}", comment); //comment仍可用
    //println!("mybook:{:?}", mybook); //=> 報錯,mybook已經被f move走了,不存在

以上3、4兩種情況,也是一樣,move無力迴天。

總結一下,在沒有實現copy trait的情況下,不管有無move:
(1)可變的自由變數進入閉包後,都無法穿越過閉包;//如 mybook
(2)不可變的自由變數是沒有問題的。//如:comment

三、為什麼要有move

想一想,如果沒有move,會如何?

use std::thread;

fn main() {
    let x = 1;
    thread::spawn(move || {
        println!("x is {}", x);
    });
}

因此,你在thread::spawn中,經常會碰到move.
如果沒有move,子執行緒則無法捕捉x變數。

四、其它情況

fn main() {
    let x = "hello";
    thread::spawn(move || {
        let y = String::from("hi,") + x;
        println!("y is {}", y);
    });
    println!("....x:{:?}", x);
    thread::sleep_ms(500000);
}

輸出:

....x:"hello"
y is hi,hello