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