Rust閉包小結

2022-10-08 18:03:17 字數 3154 閱讀 1588

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 ...