rust會根據閉包體進行環境變數的捕獲,閉包捕獲環境中變數的模式優先順序順序為:不可變借用,可變借用,所有權。
fn main() ;
println!("{}", s);
// 這個閉包的閉包體和上面僅有一處不同,那就是加了move,這會強行拿走s的所有權,即影響了捕獲方式。
// ****即使**** 閉包體看起來是用最普通的不可變引用來操作的環境中的變數,也看起來不該拿走變數的所有權
let s = string::from("gef");
let t = move || ;
// 不加注釋會編譯失敗,s所有權已經被拿走了
// println!("{}",s);
// 即使用了move,這個閉包的型別依然是fn()->boxlet ****:boxbox>=box::new(t);
let s=string::from("gef");
let t=||s;
// 這行**無法編譯,因為s的所有權被移動到閉包裡面去了,但如果閉包體是 &s,則可以編譯
// println!("{}",s)
// 無論用不用move,這個閉包的型別都是fnonce()->string, 因為閉包實現哪種trait只和如何使用捕獲到的變數有關係
// 但是閉包體的**在一定程度上影響了變數如何捕獲,rust會通過推導來決定要不要把變數的所有權轉移到閉包體內
// 對於沒有實現copy trait的型別來說,不加move的話,環境變數會不會被拿走所有權,取決於閉包體內的使用方式
// **但是如果加了move,無論閉包體怎麼寫,**獲的環境變數(未實現copy trait)一定會被拿走所有權**
}
閉包的初始化行為只會執行一次,就是在閉包定義處,而不是在閉包執行處。在這個過程中會進行變數的捕獲,且僅在閉包的定義處捕獲一次,後期執行處不再進行捕獲操作。
fn main()
即使把環境變數恢復了,閉包也不會再去捕獲了,而在閉包定義處,變數已經被移動到閉包體內了,接著在第一次執行的時候變數被消耗掉了,第二次執行的時候不會再進行變數的捕獲動作,所以變數就沒了。所以二次執行閉包會報錯。
從這裡可以看出來,如果乙個閉包只能執行一次,說明它肯定拿走了環境變數的所有權,所以這種閉包一定是fnonce型別的。
事實上fnonce是每個閉包都實現了的trait,因為每個閉包都至少能執行一次,然後根據閉包使用變數的方式逐漸特化其實現的trait。
如果閉包拿走了環境變數的所有權,導致不能二次執行,則這種閉包的型別一定是fnonce.
但是導致不能二次執行的原因可能是閉包的move關鍵字。不過move關鍵字不會影響閉包的型別,閉包實現哪種trait依然僅僅取決於閉包內部如何使用變數。比如下面這段**能夠編譯通過.
fn test(_t:impl fnonce())
fn main() ",s);
test(f);
}//
這有乙個奇妙的例子:
fn main();
println!("now a is {}", a);
inc();
}
let mut inc = || ;
| -- - first borrow occurs due to use of `a` in closure
| |
| mutable borrow occurs here
4 | println!("now a is {}", a);
| ^ immutable borrow occurs here
5 | inc();
| --- mutable borrow later used here
很明顯閉包在在初始化的時候就已經發生了借用行為。為啥會這樣呢,不是說閉包在初始化的時候不執行嗎?原因是閉包本身是個匿名結構體,初始化的時候會根據閉包體內的**把外部變數按照相應的方式捕獲進結構體,這裡顯然是mutable borrow方式;而實際執行給a+1的時間節點在閉包執行的時候,執行的本質就是匿名結構體操作自己的成員變數。所以借用是發生在初始化的,所以報了這個錯誤。
fn main()", s)};
f();
f();
}
這種閉包為什麼能多次執行,是因為s的所有權直接被拿到匿名結構體裡面去了,閉包每次實際執行push都是在匿名結構體裡面的s上執行。這也是為啥當閉包裡面有修改操作的時候閉包本身必須用mut修飾,因為執行修改操作的時候閉包本身被改了,它就必須是mut的了。
move關鍵字:
接收閉包作為引數&&返回閉包 ( 接收閉包比返回閉包多了乙個trait bounds的方式
由於impl方式不涉及到泛型,所以有些泛型限定不能用。更多時候接收閉包的**更傾向於使用trait bounds的方式。
完整的例子
fn receive1(f: t)
where
t: fn(i32) -> i32,
", num);
}fn receive2(f: impl fn(i32) -> i32) ", num);
}fn receive3(f: boxi32>) ", num);
}fn return1() -> impl fnonce(string) -> string {}", greeting, name)
}fn return2() -> boxstring> {}", greeting, name))
}fn main() ",return1()(string::from("jhon")));
println!(" {} ",return2()(string::from("nerd")))
}
receive123分別是 trait bounds ,impl trait , trait object
return12分別是 impl trait , trait object
rust學習 閉包 closuer
閉包就是匿名函式或者lambda表示式,由於rust的一些特殊語法,rust中的閉包與其他語言有些不一樣的用法,每次看過很容易忘記,特此記錄。閉包的基本定義方法是 使用 替代 將輸入變數括起來。區塊定界符 將所有函式中的表示式擴起來,如果只有一條表示式也可以不加。閉包與函式的一大區別是,閉包能夠在內...
rust筆記13 閉包
閉包是乙個可捕獲周圍環境的可執行 片段,基本的幾個定義方式如下 fn add one v1 x u32 u32 let add one v2 x u32 u32 let add one v3 x let add one v4 x x 1 內部的是捕獲的周圍的變數,預設捕獲的是不可變借用,先給出乙個實...
Rust基礎筆記 閉包
closure看上去是這樣的 let plus one x i32 x 1 assert eq 2,plus one 1 首先建立乙個繫結plus one,然後將它分配給乙個closure,body是乙個expression,注意 也是乙個expression。它也可以被寫成這樣 let plus ...