<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                Rust不只有命名函數,也有匿名函數。有一個相關的環境的匿名函數叫做“閉包”,因為它們包含在同一個環境中。正如我們將看到的,Rust里面有大量閉包的實現。 ## 語法 閉包看起來像這樣: ~~~ let plus_one = |x: i32| x + 1; assert_eq!(2, plus_one(1)); ~~~ 我們創建了一個綁定,`plus_one`,并把它賦予一個閉包。閉包的參數位于管道(`|`)之中,而閉包體是一個表達式,在這個例子中,`x + 1`。記住`{}`是一個表達式,所以我們也可以擁有包含多行的閉包: ~~~ let plus_two = |x| { let mut result: i32 = x; result += 1; result += 1; result }; assert_eq!(4, plus_two(2)); ~~~ 你會注意到閉包的一些方面與用`fn`定義的常規函數有點不同。第一個是我們并不需要標明閉包接收和返回參數的類型。我們可以: ~~~ let plus_one = |x: i32| -> i32 { x + 1 }; assert_eq!(2, plus_one(1)); ~~~ 不過我們并不必須這么寫。為什么呢?基本上,這是出于“人體工程學”的原因。因為為命名函數指定全部類型有助于像文檔和類型推斷,而閉包的類型則很少有文檔因為它們是匿名的,并且并不會產生像推斷一個命名函數的類型這樣的“遠距離錯誤”。 第二個是語法是相似的,不過有點不同。我會增加空格來使它們看起來更像一點: ~~~ fn plus_one_v1 (x: i32 ) -> i32 { x + 1 } let plus_one_v2 = |x: i32 | -> i32 { x + 1 }; let plus_one_v3 = |x: i32 | x + 1 ; ~~~ 有些小區別,不過仍然是相似的。 ## 閉包和它們的環境 之所以把它稱為“閉包”是因為它們“包含在環境中”(close over their environment)。這看起來像: ~~~ let num = 5; let plus_num = |x: i32| x + num; assert_eq!(10, plus_num(5)); ~~~ 這個閉包,`plus_num`,引用了它作用域中的`let`綁定:`num`。更明確的說,它借用了綁定。如果我們做一些會與這個綁定沖突的事,我們會得到一個錯誤。像這一個: ~~~ let mut num = 5; let plus_num = |x: i32| x + num; let y = &mut num; ~~~ 錯誤是: ~~~ error: cannot borrow `num` as mutable because it is also borrowed as immutable let y = &mut num; ^~~ note: previous borrow of `num` occurs here due to use in closure; the immutable borrow prevents subsequent moves or mutable borrows of `num` until the borrow ends let plus_num = |x| x + num; ^~~~~~~~~~~ note: previous borrow ends here fn main() { let mut num = 5; let plus_num = |x| x + num; let y = &mut num; } ^ ~~~ 一個啰嗦但有用的錯誤信息!如它所說,我們不能取得一個`num`的可變借用因為閉包已經借用了它。如果我們讓閉包離開作用域,我們可以: ~~~ let mut num = 5; { let plus_num = |x: i32| x + num; } // plus_num goes out of scope, borrow of num ends let y = &mut num; ~~~ 如果你的閉包需要它,然而,相反Rust會取得所有權并移動環境: ~~~ let nums = vec![1, 2, 3]; let takes_nums = || nums; println!("{:?}", nums); ~~~ 這會給我們: ~~~ note: `nums` moved into closure environment here because it has type `[closure(()) -> collections::vec::Vec]`, which is non-copyable let takes_nums = || nums; ^~~~~~~ ~~~ `Vec<T>`擁有它內容的所有權,并且因此,當我們在閉包中引用它時,我們必須取得`nums`的所有權。這與我們傳遞`nums`給一個取得它所有權的函數一樣。 ## `move`閉包 我們可以使用`move`關鍵字強制我們的閉包取得它環境的所有權: ~~~ let num = 5; let owns_num = move |x: i32| x + num; ~~~ 現在,即便關鍵字是`move`,變量遵循正常的移動語義。在這個例子中,`5`實現了`Copy`,所以`owns_num`取得一個`5`的拷貝的所有權。那么區別是? ~~~ let mut num = 5; { let mut add_num = |x: i32| num += x; add_num(5); } assert_eq!(10, num); ~~~ 所以在這個例子中,我們的閉包取得了一個`num`的可變引用,然后接著我們調用了`add_num`,它改變了其中的值,正如我們期望的。我們也需要將`add_num`聲明為`mut`,因為我們會改變它的環境。 如果我們改為一個`move`閉包,這有些不同: ~~~ let mut num = 5; { let mut add_num = move |x: i32| num += x; add_num(5); } assert_eq!(5, num); ~~~ 我們只會得到`5`。與其獲取一個我們`num`的可變借用,我們取得了一個拷貝的所有權。 另一個理解`move`閉包的方法:它給出了一個擁有自己棧幀的閉包。沒有`move`,一個閉包可能會綁定在創建它的棧幀上,而`move`閉包則是獨立的。例如,這意味著大體上你不能從函數返回一個非`move`閉包。 不過在我們討論獲取或返回閉包之前,我們應該更多的了解一下閉包實現的方法。作為一個系統語言,Rust給予你了成噸的控制你代碼的能力,而閉包也是一樣。 ## 閉包實現 Rust的閉包實現與其它語言有些許不用。它們實際上是特性的語法糖。在這以前你會希望閱讀[特性章節](http://doc.rust-lang.org/nightly/book/traits.html),和[靜態和動態分發](http://doc.rust-lang.org/nightly/book/trait-objects.html)(已改為特性對象章節)章節,它講到了特性對象。 都搞定啦?OK! 理解閉包底層是如何工作的關鍵有點奇怪:使用`()`調用函數,像`foo()`,是一個可重載的運算符。到此,其它的一切都會明了。在Rust中,我們使用特性系統來重載運算符。調用函數也不例外。我們有三個特性來分別重載: ~~~ pub trait Fn : FnMut { extern "rust-call" fn call(&self, args: Args) -> Self::Output; } pub trait FnMut : FnOnce { extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; } pub trait FnOnce { type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output; } ~~~ 你會注意到這些特性間的些許區別,不過一個大的區別是`self`:`Fn`獲取`&self`,`FnMut`獲取`&mut self`,而`FnOnce`獲取`self`。這包含了所有3種通過通常函數調用語法的`self`。不過我們將它們分在3個特性里,而不是單獨的1個。這給了我們大量的對于我們可以使用哪種閉包的控制。 閉包的`|| {}`語法是上面3個特性的語法糖。Rust將會為了環境創建一個結構體,`impl`合適的特性,并使用它。 ## 閉包作為參數 現在我們知道了閉包是特性,我們已經知道了如何接受和返回閉包;就像任何其它的特性! 這也意味著我們也可以選擇靜態或動態分發。首先,讓我們寫一個獲取可調用結構的函數,調用它,然后返回結果: ~~~ fn call_with_one(some_closure: F) -> i32 where F : Fn(i32) -> i32 { some_closure(1) } let answer = call_with_one(|x| x + 2); assert_eq!(3, answer); ~~~ 我們傳遞我們的閉包,`|x| x + 2`,給`call_with_one`。它正做了我們說的:它調用了閉包,`1`作為參數。 讓我們更深層的解析`call_with_one`的簽名: ~~~ fn call_with_one(some_closure: F) -> i32 ~~~ 我們獲取一個參數,而它有類型`F`。我們也返回一個`i32`。這一部分并不有趣。下一部分是: ~~~ where F : Fn(i32) -> i32 { ~~~ 因為`Fn`是一個特性,我們可以用它限制我們的泛型。在這個例子中,我們的閉包取得一個`i32`作為參數并返回`i32`,所以我們用泛型限制是`Fn(i32) -> i32`。 還有一個關鍵點在于:因為我們用一個特性限制泛型,它會是單態的,并且因此,我們在閉包中使用靜態分發。這是灰常簡單的。在很多語言中,閉包固定在堆上分配,所以總是進行動態分發。在Rust中,我們可以在棧上分配我們閉包的環境,并靜態分發調用。這經常發生在迭代器和它們的適配器上,它們經常取得閉包作為參數。 當然,如果我們想要動態分發,我們也可以做到。特性對象處理這種情況,通常: ~~~ fn call_with_one(some_closure: &Fn(i32) -> i32) -> i32 { some_closure(1) } let answer = call_with_one(&|x| x + 2); assert_eq!(3, answer); ~~~ 現在我們取得一個特性對象,一個`&Fn`。并且當我們將我們的閉包傳遞給`call_with_one`時我們必須獲取一個引用,所以我們試用`&||`。 ## 返回閉包 對于函數式風格代碼來說在各種情況返回閉包是非常常見的。如果你嘗試返回一個閉包,你可能會得到一個錯誤。在剛接觸的時候,這看起來有點奇怪,不過我們會搞清楚。當你嘗試從函數返回一個閉包的時候,你可能會寫出類似這樣的代碼: ~~~ fn factory() -> (Fn(i32) -> Vec) { let vec = vec![1, 2, 3]; |n| vec.push(n) } let f = factory(); let answer = f(4); assert_eq!(vec![1, 2, 3, 4], answer); ~~~ 編譯的時候會給出這一長串相關錯誤: ~~~ error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn(i32) -> collections::vec::Vec` [E0277] f = factory(); ^ note: `core::ops::Fn(i32) -> collections::vec::Vec` does not have a constant size known at compile-time f = factory(); ^ error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn(i32) -> collections::vec::Vec` [E0277] factory() -> (Fn(i32) -> Vec) { ^~~~~~~~~~~~~~~~~~~~~ note: `core::ops::Fn(i32) -> collections::vec::Vec` does not have a constant size known at compile-time fa ctory() -> (Fn(i32) -> Vec) { ^~~~~~~~~~~~~~~~~~~~~ ~~~ 為了從函數返回一些東西,Rust需要知道返回類型的大小。不過`Fn`是一個trait,它可以是各種大小(size)的任何東西。比如說,返回值可以是實現了`Fn`的任意類型。一個簡單的解決方法是:返回一個引用。因為引用的大小(size)是固定的,因此返回值的大小就固定了。因此我們可以這樣寫: ~~~ fn factory() -> &(Fn(i32) -> Vec) { let vec = vec![1, 2, 3]; |n| vec.push(n) } let f = factory(); let answer = f(4); assert_eq!(vec![1, 2, 3, 4], answer); ~~~ 不過這樣會出現另外一個錯誤: ~~~ error: missing lifetime specifier [E0106] fn factory() -> &(Fn(i32) -> i32) { ^~~~~~~~~~~~~~~~~ ~~~ 對。因為我們有一個引用,我們需要給它一個生命周期。不過我們的`factory()`函數不接收參數,所以省略不能用在這。我們可以使用神馬生命周期呢?`'static`: ~~~ fn factory() -> &'static (Fn(i32) -> i32) { let num = 5; |x| x + num } let f = factory(); let answer = f(1); assert_eq!(6, answer); ~~~ 不過這樣又會出現另一個錯誤: ~~~ error: mismatched types: expected `&'static core::ops::Fn(i32) -> i32`, found `[closure <anon>:7:9: 7:20]` (expected &-ptr, found closure) [E0308] |x| x + num ^~~~~~~~~~~ ~~~ 這個錯誤讓我們知道我們并沒有返回一個`&'static Fn(i32) -> i32`,而是返回了一個`[closure <anon>:7:9: 7:20]`。等等,什么? 因為每個閉包生成了它自己的環境`struct`并實現了`Fn`和其它一些東西,這些類型是匿名的。它們只在這個閉包中存在。所以Rust把它們顯示為`closure <anon>`,而不是一些自動生成的名字。 不過為什么我們的閉包沒有實現`&'static Fn`呢?正如我們之前討論的,閉包借用了它們所在的環境。而在這個例子中,我們的環境是基于棧分配的,跟`num`變量綁定的`5`。所以這個借用有一個在棧幀中的生命周期。如果我們返回這個閉包,這一函數調用將會結束,棧幀將會消失,而我們的閉包獲取到了一個垃圾內存的環境! 那么我們該怎么做?這個_幾乎_可以成功運行了: ~~~ fn factory() -> Box i32> { let num = 5; Box::new(|x| x + num) } let f = factory(); let answer = f(1); assert_eq!(6, answer); ~~~ 我們使用一個trait對象,通過`Box`把`Fn`裝箱。不過還有最后一個錯誤: ~~~ error: `num` does not live long enough Box::new(|x| x + num) ^~~~~~~~~~~ ~~~ 我們仍有一個指向父棧幀的引用。加上這一個最后的修改后,這段代碼可以成功運行了: ~~~ fn factory() -> Box<Fn(i32) -> i32> { let num = 5; Box::new(move |x| x + num) } let f = factory(); let answer = f(1); assert_eq!(6, answer); ~~~ 通過把內部閉包變為`move Fn`,我們為閉包創建了一個新的棧幀。通過`Box`裝箱,我們提供了一個已知大小的返回值,并允許它離開我們的棧幀。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看