WilsonKuo
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- tags: RUST LANGUAGE --- # [Crust of Rust](/jLixlQ1ASZaBHqCBDFu1Iw) : Functions, Closures, and Their Traits ==[直播錄影](https://www.youtube.com/watch?v=QVK4Ooo_PqM)== * 主機資訊 ```rust wilson@wilson-HP-Pavilion-Plus-Laptop-14-eh0xxx ~/CrustOfRust> neofetch --stdout wilson@wilson-HP-Pavilion-Plus-Laptop-14-eh0xxx ----------------------------------------------- OS: Ubuntu 22.04.3 LTS x86_64 Host: HP Pavilion Plus Laptop 14-eh0xxx Kernel: 6.2.0-37-generic Uptime: 22 mins Packages: 2367 (dpkg), 11 (snap) Shell: bash 5.1.16 Resolution: 2880x1800 DE: GNOME 42.9 WM: Mutter WM Theme: Adwaita Theme: Yaru-dark [GTK2/3] Icons: Yaru [GTK2/3] Terminal: gnome-terminal CPU: 12th Gen Intel i5-12500H (16) @ 4.500GHz GPU: Intel Alder Lake-P Memory: 8569MiB / 15695MiB ``` * Rust 編譯器版本 : ```rust wilson@wilson-HP-Pavilion-Plus-Laptop-14-eh0xxx ~/CrustOfRust> rustc --version rustc 1.70.0 (90c541806 2023-05-31) (built from a source tarball) ``` ## Introduction [0:00:00](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=0s) In this episode, we go over the differences between function items, function pointers, and closures, as well as how they interact with the Fn* traits. We also touch upon dynamically dispatched Fns and experimental const Fn bounds. ## Rust for Rustaceans [0:01:20](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=80s) Jon 寫了 [Rust for Rustaceans](https://nostarch.com/rust-rustaceans),讀這本書之前必須先有先備知識,至少要看過 [The Rust Programming Language](https://doc.rust-lang.org/book/)。 ## Function items [0:02:48](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=148s) 開始建置 Rust 專案 : ``` shell $ cargo new --bin call-me $ cd eksempel $ vim src/main.rs ``` 看到主程式 : ```rust= fn main() { // main 是個函式,main 也有型別 println!("Hello, world!"); } ``` 觀察 `bar` 函式 : ```rust= fn main() { println!("Hello, world!"); let x = bar; // x 的型別為 fn bar(),看似函式指標,實則並不然。 // 它實際上是 function item,function item 與函式指標有點不同。 // 首先 function item 在編譯時期僅用於參考唯一函式 bar 的 0-sized 值。 } fn bar() {} ``` 如果 `bar` 函式為泛型,我們就不能將 `bar` 函式指派給變數 : ```diff= fn main() { println!("Hello, world!"); let x = bar; } -fn bar() {} +fn bar<T>() {} ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let x = bar; } fn bar<T>() {} ``` ::: 編譯出現以下錯誤 : ```rust $ cargo run ... error[E0282]: type annotations needed --> src\main.rs:3:13 | 3 | let x = bar; | ^^^ cannot infer type of the type parameter `T` declared on the function `bar` | help: consider specifying the generic argument | 3 | let x = bar::<T>; | +++++ ``` 編譯器有提示你需要傳入泛型參數。 嘗試重新將 x 指向使用不同泛型參數 instantiate 的 function item : ```diff= fn main() { println!("Hello, world!"); - let mut x = bar; + let mut x = bar::<i32>; x = bar::<u32>; } fn bar<T>() {} ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; x = bar::<u32>; } fn bar<T>() {} ``` ::: 編譯器會提示錯誤 : ```rust $ cargo run ... error[E0308]: mismatched types --> src\main.rs:4:9 | 3 | let mut x = bar::<i32>; | ---------- expected due to this value 4 | x = bar::<u32>; | ^^^^^^^^^^ expected `i32`, found `u32` | = note: expected fn item `fn() {bar::<i32>}` found fn item `fn() {bar::<u32>}` = note: different fn items have unique types, even if their signatures are the same = help: consider casting both fn items to fn pointers using `as fn()` ``` 會有這個錯誤是因為,`x` 並不是函式指標,而是 function item。 實際印出 x 占用的記憶體空間 : ```diff= fn main() { println!("Hello, world!"); let mut x = bar; + println!("{}", std::mem::size_of_val(&x)); - x = bar::<u32>; + // x = bar::<u32>; } fn bar<T>() {} ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; } fn bar<T>() {} ``` ::: 編譯並執行程式真的得到 0-sized : ```rust= $ cargo run ... Hello, world! 0 ``` `x` 根本不持有指標,因為 `bar::<i32>` 只是編譯器用來識別此函式的唯一 instance 的 identifier。 ## Function pointers [0:06:26](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=386s) Function item 具有為它們定義的強制轉換為函式指標 : ```diff= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; + baz(bar::<i32>); + baz(bar::<u32>); } -fn bar<T>() {} +fn bar<T>(_: u32) -> u32 { 0 } +fn baz(f: fn(u32) -> u32 ) {} ``` baz 接收的型別為函式指標,這時的 Line 6 與 Line 7 編譯器會將它們從 function item 強制轉換成函式指標。 驗證是否轉為函式指標,印出 baz 接收到的參數所占的記憶體大小 : ```diff= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<u32>); baz(bar::<i32>); } fn bar<T>(_: u32) -> u32 { 0 } -fn baz(f: fn(u32) -> u32 ) {} +fn baz(f: fn(u32) -> u32 ) { + println!("{}", std::mem::size_of_val(&f)); +} ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); } fn bar<T>(_: u32) -> u32 { 0 } fn baz(f: fn(u32) -> u32 ) { println!("{}", std::mem::size_of_val(&f)); } ``` ::: 編譯並執行可以看到編譯器真的將 function item 強制轉換成函式指標 : ```rust= $ cargo check ... Hello, world! 0 8 8 ``` 這個轉換是必要的,因為這兩個函式為不同的型別的 `bar` 函式 instantiations,這意味著它們具有不同的程式碼,它們已經被單獨最佳化了,它們是不同的程式碼塊,恰好共享一個泛用名稱。 當我們第一次呼叫 `baz` 函式時,我們需要將指標傳遞給函式的一個 instance 的第一個指令,而第二次呼叫 `baz` 函式時,則需要傳遞一個不同的函式指標。 `fn baz(f: fn(u32) -> u32 )` 確實允許我們對傳入的確切函式進行泛型化,只要它**具有相同的簽章**,若將 bar 的函式簽章做修改將會產生編譯錯誤 : ```diff= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); } -fn bar<T>(_: u32) -> u32 { 0 } +fn bar<T>() {} fn baz(f: fn(u32) -> u32 ) { println!("{}", std::mem::size_of_val(&f)); } ``` 編譯出現以下錯誤 : ```rust= $ cargo run ... error[E0308]: mismatched types --> src\main.rs:6:9 | 6 | baz(bar::<i32>); | --- ^^^^^^^^^^ incorrect number of function parameters | | | arguments to this function are incorrect | = note: expected fn pointer `fn(u32) -> u32` found fn item `fn() {bar::<i32>}` ... ``` 我們從中學到的是,function item 和函式指標彼此不同,但 function item 可以強制轉換為函式指標。編譯器這麼做的原因如下 : ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); } fn bar<T>(_: u32) -> u32 { 0 } fn baz(f: fn(u32) -> u32 ) { println!("{}", std::mem::size_of_val(&f)); } ``` 如果沒有在 Line 6 與 Line 7 呼叫 `baz` 函式,即使我們將具 `T` 的 `bar` 函式 instantiat 為 `i32`,編譯器實際上並不需要產生該函式的程式,它並沒有義務這樣做,因為 `bar::<i32>` 從未被呼叫。 如果在 Line 6 與 Line 7 呼叫 `baz` 函式,編譯器將此強制轉換為指標的原因是,它需要能夠產生一個指向該函式 body 的指標值。因此,編譯器必須產生該函式的程式碼,以便首先產生該函式指標。 結論 : Function item 唯一標識函式的特定 instance,而函式指標則是指向具有特定簽章的函式的指標,你可以將一個 function item 轉換為函式指標,但不能反向進行轉換。 :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } ``` ::: ## Function traits [0:11:24](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=684s) 接著看到 `Fn()` : ```rust= fn main() { ... quox(bar::<u32>); } fn bar<T>() {} ... fn quox<F>(f : F) where F: Fn(), { } ``` `Fn()` 與 `fn()` 是不一樣的東西,雖然它們的建構方式相同。`fn()` 作為函式指標使用,`Fn()` 則是 trait。 * [Trait std::ops::Fn](https://doc.rust-lang.org/std/ops/trait.Fn.html) 的簽章如下 : ```rust= pub trait Fn<Args>: FnMut<Args> where Args: Tuple, { // Required method extern "rust-call" fn call(&self, args: Args) -> Self::Output; // 傳入 self 的參考 } ``` `Fn()` 在某種程度上相當於擁有共享參考,因為你可以多次呼叫它,並且你可以透過共享參考同時多次呼叫它,或者至少是透過一個共享參考。 * [Trait std::ops::FnMut](https://doc.rust-lang.org/std/ops/trait.FnMut.html) 的簽章如下 : ```rust= pub trait FnMut<Args>: FnOnce<Args> where Args: Tuple, { // Required method extern "rust-call" fn call_mut( &mut self, args: Args ) -> Self::Output; // 傳入 self 的 exclusive 參考 } ``` `FnMut()` 的話,你一次只能呼叫一次。所以,首先你需要一個 mutable 參考。如果你有一個 mutable 參考,**你可以多次呼叫它,但一次只能呼叫一次**,這點很重要。例如,如果你將 `FnMut()` 插入 Rc,則將無法呼叫它。同樣,如果你提供了對 `FnMut()` 的共享參考,你也無法呼叫它。這也是為什麼一般情況下,如果你透過 [rayon](https://docs.rs/rayon/latest/rayon/) 之類的東西將它傳遞給多個執行緒,那麼你就不能同時為多個執行緒呼叫 `FnMut`,因為它不是 exclusive。 * [Trait std::ops::FnOnce](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) 的簽章如下 : ```rust= pub trait FnOnce<Args> where Args: Tuple, { type Output; // Required method extern "rust-call" fn call_once(self, args: Args) -> Self::Output; // 傳入 owned self } ``` 從 `FnOnce()` 的命名得知,你只能呼叫它一次。你已經移動了你想要呼叫的函式的值,你再也無法再次呼叫它。 一旦我們了解什麼是 closures 以及它們的作用,原因就會變得清晰。讓我們先具體考慮一下這對於 function item 和函式指標意味著什麼。 function item 和函式指標並沒有狀態,它們只是獨立的程式碼區塊,並且不參考任何其他 stack frame 中的任何內容。它們不參考儲存在自身外部的任何記憶體。它們不與生命週期相關聯,這意味著它們實際上不關心 `self`。對於 function item 來說,`self` 中不包含任何東西。對於函式指標來說,`self` 只是指向函式的指標。但實際上沒有狀態可變。沒有東西可移動。 因此,function item 強制轉換為函式指標,而函式指標實作了 `Fn`/ `FnMut`/ `FnOnce` trait。因此,如果你有一個函式指標,則可以將其傳遞給接受 `Fn`/ `FnMut`/ `FnOnce` 的函式。 這些 `Fn` trait 的一種思考方式是,它們是一種階層結構 : ![image](https://hackmd.io/_uploads/B1dy5DVi6.png) 對應文件 : > Since both `Fn` and `FnMut` are **subtraits** of `FnOnce`, any instance of `Fn` or `FnMut` can be used where a `FnOnce` is expected. 根據文件的敘述可以得知 `FnOnce` 實作了 `Fn` : ```rust= impl<F> FnOnce for F where F: Fn(), { fn call(self) {} } ``` 我們只談到了函式指標,但如果我們有一個對舊 owned `self` 的參考,或者一個 owned `self`,那麼我們可以輕而易舉地產生一個對 `self` 的參考。因此,我們可以輕鬆地將 `self` 作為 `Fn` 呼叫。就像我可以輕鬆地將一個轉換為另一個一樣。因為如果我有一個實作了 `Fn` 的東西,並且我有一個 `self`,我只需將其取為參考並將其傳遞給 `Fn` : ```rust= impl<F> FnOnce for F where F: Fn(), { fn call(self) { Fn::call(&self); } } ``` `FnOnce` 也可以實作出 `FnMut` : ```rust= impl<F> FnOnce for F where F: FnMut(), { fn call(mut self) { FnMut::call(&mut self); } } ``` `FnMut` 則可以實作出 `Fn` : ```rust= impl<F> FnMut for F where F: Fn(), { fn call(&mut self) { Fn::call(&*self); } } ``` 函式指標實作了 `Fn`/ `FnMut`/ `FnOnce` trait。因為它實際上不關心函式指標的所有權,因為它只是指向程式碼的指標。所以前面的 `quox` 函式的 bound 可以是 `Fn`/ `FnMut`/ `FnOnce` 其中一個。 * 如果是 quox 函式使用到 `FnOnce` bound,若傳入 exclusive 參考,你不能呼叫 `f`,因為你並沒有 `f` 的擁有權 : ```rust= fn main() { ... quox(&mut bar::<u32>); } fn quox<F>(f : &mut F) where F: FnOnce(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&mut bar::<u32>); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &mut F) where F: FnOnce(), { (f)() } ``` ::: 編譯會出現以下錯誤,因為傳入的並不是 owned,只是 mutable 參考 : ```rust= $ cargo run ... error[E0507]: cannot move out of `*f` which is behind a mutable reference --> src\main.rs:29:5 | 29 | (f)() | ^^^-- | | | `*f` moved due to this call | move occurs because `*f` has type `F`, which does not implement the `Copy` trait ... ``` 如果你將 bound 改為 FnMut 就可以呼叫 `f` : ```rust= fn main() { ... quox(&mut bar::<u32>); } fn quox<F>(f : &mut F) where F: FnMut(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&mut bar::<u32>); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &mut F) where F: FnMut(), { (f)() } ``` ::: * 如果是 quox 函式使用到 `FnMut` bound,若傳入共享參考,你不能呼叫 `f`,因為你並沒有 `f` 的 mutable 參考 : ```rust= fn main() { ... quox(&bar::<u32>); } fn quox<F>(f : &F) where F: FnMut(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: FnMut(), { (f)() } ``` ::: 編譯會出現以下錯誤,因為傳入的並不是 mutable 參考,只是共享參考 : ```rust= $ cargo run ... error[E0596]: cannot borrow `*f` as mutable, as it is behind a `&` reference --> src\main.rs:29:5 | 29 | (f)() | ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable ... ``` 如果你將 bound 改為 Fn 就可以呼叫 `f` : ```rust= fn main() { ... quox(&bar::<u32>); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: ## Closures [0:20:40](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=1240s) 為什麼我們需要 `Fn`/ `FnMut`/ `FnOnce` 這些差異 ? 一旦你開始談論 closure,差異就會出現。 首先看到 non-capturing closure : ```rust= fn main() { let f = |x: i32, y:i32| x + y; } ``` closures 之所以被稱為 closure 是因為它們封閉了它們的環境。它們能夠從其環境中 capture 東西,並生成一個唯一的函式。這些函式在創建時會具體吸收、使用或參考周圍環境中的資料。這個例子,它不會從環境中 capture 任何內容。它僅參考自己的參數。因此這是一個 non-capturing closure。 non-capturing closure 的有趣之處在於它們可以強制轉換為函式指標,並且可以有三種 trait 的 bound : ```rust= fn main() { ... let f = || (); baz(f); quox(&f); } ... fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); bazbar::<u32>); quox(&bar::<u32>); let f = || (); baz(f); quox(&f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: 如果是 `f` 是 capturing closure 就不行 : ```rust= fn main() { ... let z = String::new(); // 用 not Copy 的型別比較好說明 let f = || { let _ = z ; // 耗用 z }; baz(f); quox(&f); } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let z = String::new(); let f = || { let _ = z ; }; baz(f); quox(&f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: 編譯產生以下錯誤 : ```rust $ cargo run ... error[E0308]: mismatched types --> src\main.rs:13:9 | 10 | let f = || { | -- the found closure ... 13 | baz(f); | --- ^ expected fn pointer, found closure | | | arguments to this function are incorrect | = note: expected fn pointer `fn()` found closure `[closure@src\main.rs:10:13: 10:15]` note: closures can only be coerced to `fn` types if they do not capture any variables ... ``` 看到倒數第二行的說明,而我們程式的 `f` 真的有 capture 東西 (`z`)。 即便是只拿 `z` 的共享參考而不是移動 `z`,並將之列印出來也會產生相同編譯錯誤訊息 : ```rust= fn main() { ... let z = String::new(); let f = || { println!("{}", z); }; baz(f); quox(&f); } ``` `f` closure 仍會 capture 環境,這意味著你不能將 `f` closure 表示為函式指標。你可以將其想像成編譯器為 closure 產生了一種匿名結構,其中包含了從其環境中 capture 的所有欄位,當該 closure 執行時,它需要存取那些資訊 你可以把它想像成一個 `f` closure 結構,並且實作 `Fn` : ```rust= fn main() { ... let z = String::new(); let f = || { println!("{}", z); }; struct FClosure<'scope > { z: &'scope String, } impl<'scope> Fn() for FClosure<'scope> { fn call(&self) { // copy-paste from closure-definition println!("{}", self.z); } } baz(f); quox(&f); } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let z = String::new(); let f = || { println!("{}", z); }; // struct FClosure<'scope > // { // z: &'scope String, // } // impl<'scope> Fn() for FClosure<'scope> // { // fn call(&self) // { // // copy-paste from closure-definition // println!("{}", self.z); // } // } baz(f); quox(&f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: 你實際上無法編寫這段程式碼,但你可以將其視為編譯器產生的內容。因此你明白為什麼它需要存取 `z` 了。函式指標(只是指向此程式碼區塊的開頭的指標)是不夠的,**它需要 `z` 的附加狀態**。 將 `z` 改成 mut : ```rust= fn main() { ... let mut z = String::new(); let f = || { z.clear(); }; // struct FClosure<'scope > // { // z: &'scope mut String, // 需要 exclusive 參考 // } // impl<'scope> Fn() for FClosure<'scope> // { // fn call(&self) // { // // copy-paste from closure-definition // self.z.clear(); // & &mut String -> &String // // 一個對字串的 exclusive 參考的共享參考, // // 只能用作字串的共享參考。 // } // } baz(f); quox(&f); } ``` Line 16 無法運作,因為 `z` 要求 mutable 參考,因為我們假設現在是 `Fn`,而 `Fn` 擁有的只是 `&self`。 :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = || { z.clear(); }; // struct FClosure<'scope > // { // z: &'scope mut String, // } // impl<'scope> Fn() for FClosure<'scope> // { // fn call(&self) // { // // copy-paste from closure-definition // self.z.clear(); // } // } baz(f); quox(&f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: :::success :pencil2: [String::clear(&mut self)](https://doc.rust-lang.org/std/string/struct.String.html#method.clear) Truncates this String, removing all contents. While this means the String will have a length of zero, it **does not touch its capacity**. ::: 如果現在是 `FnMut`,那我們得到的就會是 mutable 參考 : ```rust= fn main() { ... let mut z = String::new(); let f = || { z.clear(); }; // struct FClosure<'scope > // { // z: &'scope mut String, // 需要 exclusive 參考 // } // // 任何實作了 FnMut 的東西都實作了 FnOnce, // // 因此這裡也有實作 FnOnce。 // impl<'scope> FnMut() for FClosure<'scope> // { // fn call(&mut self) // { // // copy-paste from closure-definition // self.z.clear(); // } // } baz(f); // 編譯器提示錯誤 : // expected fn pointer, found closure // 並沒有強制轉換成函式指標, // 因為 `f` capture 了環境。 quox(&f); // 編譯器提示錯誤 : // expected a closure that implements the `Fn` trait, // but this closure only implements `FnMut` // f 無法實作 Fn,因為它需要對其環境進行 mutably borrow。 } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = || { z.clear(); }; // struct FClosure<'scope > // { // z: &'scope mut String, // } // impl<'scope> FnMut() for FClosure<'scope> // { // fn call(&mut self) // { // // copy-paste from closure-definition // self.z.clear(); // } // } baz(f); quox(&f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f : &F) where F: Fn(), { (f)() } ``` ::: 將 `quox` 函式的 bound 改成 `FnMut()` : ```diff= fn main() { ... let mut z = String::new(); let f = || { z.clear(); }; - baz(f); + // baz(f); // 為了說明 quox,先將這行註解掉 - quox(&f); + quox(f); } ... -fn quox<F>(f : &F) +fn quox<F>(mut f: F) - where F: Fn(), + where F: FnMut(), { (f)() } ``` 由於 `f` 為 `FnMut`,且 quox 要求的 bound 又是 `FnMut`,這次就可以編譯成功了。 :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = || { z.clear(); }; // struct FClosure<'scope > // { // z: &'scope mut String, // } // impl<'scope> FnMut() for FClosure<'scope> // { // fn call(&mut self) // { // // copy-paste from closure-definition // self.z.clear(); // } // } // baz(f) quox(f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(mut f: F) where F: FnMut(), { (f)() } ``` ::: 接著探討 `FnOnce` : ```rust= fn main() { ... let mut z = String::new(); let f = || { drop(z); // 為了卸除 z 我們必須有 z 的擁有權 // 所以我們需要將 z 移動到 closure 內。 // 你只能移動 z 到這個 closure 一次 }; // struct FClosure<'scope > // { // z: String // 需要擁有字串 // } // impl<'scope> FnMut() for FClosure<'scope> // { // fn call(&mut self) // { // // copy-paste from closure-definition // drop(self.z); // // 不可能在這裡卸除 z, // // 因為我們只有 z 的 mutable 參考。 // } // } // baz(f); quox(f); } ``` 假設 closure 是 `FnOnce` 就沒有問題 (現在 closure 仍為 `FnMut`) : ```rust= fn main() { ... let mut z = String::new(); let f = || { drop(z); // 編譯器錯誤提示訊息 : // this closure implements `FnOnce`, not `FnMut` // // 為了卸除 z 我們必須有 z 的擁有權 // 所以我們需要將 z 移動到 closure 內。 // // 你只能移動 z 到這個 closure 一次 }; // struct FClosure<'scope > // { // z: String // 需要擁有字串 // } // impl<'scope> FnOnce() for FClosure<'scope> // { // fn call(self) // { // // copy-paste from closure-definition // drop(self.z); // // 可以在這裡卸除 z, // // 因為我們有 z 擁有權。 // } // } // baz(f); quox(f); } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = || { drop(z); }; // struct FClosure<'scope > // { // z: &'scope mut String, // } // impl<'scope> FnMut() for FClosure<'scope> // { // fn call(&mut self) // { // // copy-paste from closure-definition // self.z.clear(); // } // } // baz(f) quox(f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(mut f: F) where F: FnMut(), { (f)() } ``` ::: 使用 move 即可將值移入 closure : ```diff= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); - let f = || { + let f = move || { println!("{}", z); + // z is dropped here }; // baz(f) quox(f); } ... -fn quox<F>(mut f: F) +fn quox<F>(f: F) - where F: FnMut(), + where F: FnOnce(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = move || { drop(z); }; // baz(f) quox(f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f: F) where F: FnOnce(), { (f)() } ``` ::: ## Non-static closures [0:33:49](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=2029s) 如果你沒聲明 closure 的生命週期,編譯器預設它是 static : ```rust= fn make_fn() -> impl Fn() { let z = String::new(); || { println!("{}", z); // borrow z, // 無法編譯,因為 z 的生命週期是函式存活的時間。 } } ``` 若你將 `z` 值移動到 closure 內,就可以編譯 : ```rust= fn make_fn() -> impl Fn() { let z = String::new(); move || { println!("{}", z); // 這個 closure 現在擁有 z, // 且 z 的生命週期跟 closure 一樣。 } } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = move || { drop(z); }; // baz(f) quox(f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(f: F) where F: FnOnce(), { (f)() } pub fn make_fn() -> impl Fn() { let z = String::new(); move || { println!("{}", z); } } ``` ::: 嘗試在 closure 裡卸除 z : ```rust= fn make_fn() -> impl Fn() { let z = String::new(); move || { drop(z); // 編譯器提示錯誤 : // move occurs because `z` has type `String`, // which does not implement the `Copy` trait // 因為回傳型態是 Fn 而不是 FnOnce。 } } ``` 移動的一個缺點是會移動一切 closure 用到的東西 : ```rust= fn make_fn() -> impl Fn() { let x = String::new(); let z = String::new(); move || { println!("{}", x); println!("{}", z); } } ``` 你不總是想要移動一切,避免移動一切的作法有 : * 能不要用 `move` 就不要用 : ```rust= fn make_fn() -> impl Fn() { let x = String::new(); let z = String::new(); || { println!("{}", x); println!("{}", z); } } ``` * 利用間接的方式,也就是僅傳入參考 : ```rust= fn make_fn() -> impl Fn() { let x = String::new(); let z = String::new(); let x2 = &x; // 這個例子不會運作,因為 x2 生命週期並非 static, // 但這裡想表達的是你可以使用這種方法來避免移動一切。 move || { println!("{}", x2); println!("{}", z); } } ``` * 如果你不在意舊值,可以使用 shadowing,引進新的 scope : ```rust= // 這個例子不會運作,原因 : // 1. x 生命週期並非 static // 2. 回傳型態為 () // 但這裡想表達的是你可以使用這種方法來避免移動一切。 fn make_fn() -> impl Fn() { let x = String::new(); let z = String::new(); { let x = &x; // 為了只移動參考 move || { println!("{}", x2); println!("{}", z); } } } ``` Q : wait does that code create a new String in 'static, every time make_fn(I) is called? A : 是的,如下 : ```rust= pub fn make_fn() -> impl Fn() { let z = String::new(); // 建立新的 String move || { println!("{}", z); // closure 擁有 z // 當 closure 被卸除時,z 才會被卸除。 } } ``` ## dyn Fn [0:38:50](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=2330s) 接著看 trait 物件,`Fn`/ `FnMut`/ `FnOnce` 皆可被 dynamic dispatch : * `dyn Fn()` ```rust= fn hello(f: Box<dyn Fn()>) { f() } ``` * `dyn FnMut()` ```rust= fn hello(mut f: Box<dyn FnMut()>) { f() } ``` * `dyn FnOnce()` ```rust= fn hello(mut f: Box<dyn FnOnce()>) { f() } ``` 繼續看到 `Box<dyn Fn_>` : ```rust= // Box<dyn Fn()> did not implement Fn() // Box<dyn FnMut()> did not implement FnMut() // Box<dyn FnOnce()> did not implement FnOnce() ``` 上述的三種情況的原因很有趣,假設我們想實作 `Fn_` : ```rust= impl FnOnce() for Box<dyn FnOnce()> { fn call(self) { // self = Box, self.0 取 Box 裡面的值 let x: dyn FnOnce() = self.0; // x 的型別是 dyn FnOnce(), // 但 dyn FnOnce() 不是 Sized, // x 究竟占據 stack 記憶體多少空間 ? x.call() } } ``` :::success :pencil2: [Fn* closure traits implemented for Box<dyn Fn*>](https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html#fn-closure-traits-implemented-for-boxdyn-fn) This was ultimately due to a limitation in the compiler's ability to reason about such implementations, which has since been fixed with the introduction of [unsized locals](https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html#fn-closure-traits-implemented-for-boxdyn-fn). ::: ::: warning :question: [0:42:42](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=2562s) 繼續看到 `Fn_` 實作 : ```rust= impl FnOnce() for Box<dyn FnOnce()> { fn call(self) // 你可能會問,為什麼不拿 Box 內容的共享參考 ? { // 如果你要呼叫 x, // 你必須擁有 self,你不能只拿到 self 的共享參考 let x: &dyn FnOnce() = &self.0; x.call() } } ``` :::success :pencil2: [Tracking issue for RFC #1909: Unsized Rvalues (unsized_locals, unsized_fn_params) #48055](https://github.com/rust-lang/rust/issues/48055) 有一個專門針對 Unsized Rvalues 的 RFC,這是此實作存在所必需的。RFC 已經完成,但還有很多實作問題,基本上,編譯器可以利用這個特定的功能,但這是不穩定的。你可以在 nightly 版本中選擇使用它,但通常不能在穩定版本上使用這個功能。但這對於這個特定的功能是必要的,這是一個有趣的小插曲。 ::: 當你擁有一個 `Box<dyn Fn>` 這樣的型別時,現在它是有效的,你不需要特別對待它,一般來說,`dyn` 就能正常工作。 ::: 嘗試將 `f` 變成 `&dyn Fn()` 是可以的 : ```rust= fn main() { ... // baz(f) let f: &dyn Fn() = &f; // 這有點像函式指標,但它被允許有一個 self。 // 基本上它建構了一個用於這個 closure 的 cell, // 然後將其用作 dynamic dispatch 的資料指標, // 而 vtable 只有 call 方法。 quox(f); } ... fn quox<F>(mut f: F) where F: FnMut(), { (f)() } ``` :::spoiler {state="close"} 目前程式碼 ```rust= fn main() { println!("Hello, world!"); let mut x = bar::<i32>; println!("{}", std::mem::size_of_val(&x)); // x = bar::<u32>; baz(bar::<i32>); baz(bar::<u32>); quox(&bar::<u32>); let mut z = String::new(); let f = || { println!("{}", z); }; // baz(f) let f: &dyn Fn() = &f; quox(f); } fn bar<T>() {} fn baz(f: fn()) { println!("{}", std::mem::size_of_val(&f)); } fn quox<F>(mut f: F) where F: FnMut(), { (f)() } pub fn make_fn() -> impl Fn() { let z = String::new(); move || { println!("{}", z); } } ``` ::: 但若將 `f` 變成 `&dyn FnMut()` 就不可以,原因如下 : ```rust= fn main() { ... // baz(f) // 現在只有 f 的共享參考, // 所以 &dyn FnMut() 並未實作 FnMut,只實作 Fn。 let f: &dyn FnMut() = &f; quox(f); } ... fn quox<F>(mut f: F) where F: FnMut(), { (f)() } ``` 當你使用 dynamic dispatch 的 `Fn_` trait 時,你需要確保所使用的 wrapper、間接型別、寬指標型別允許你實際上需要的存取方式,以便呼叫該函式 : ```rust= let f: &mut dyn FnMut() = &f; let f: &dyn FnMut() = &f; let f: Box<dyn FnOnce()> = Box::new(f); // ok,因為 FnOnce 的限制比 FnMut 還嚴格。 // let f: Box<dyn FnMut()> = Box::new(f); // ok,因為 FnOnce 的限制比 Fn 還嚴格。 // let f: Box<dyn Fn()> = Box::new(f); // Arc 尚未支援 !!! // let f: std::sync::Arc<dyn Fn()> = std::sync::Arc::new(f); // Arc 允許你將 Unsized 的事物放入其中, // 但是出於必要,Arc 只會給你對內部事物的共享存取權限, // 因此 Arc<dyn Fn()> 實作了 Fn, // 但是 Arc<dyn FnMut()> 仍然只實作了 Fn,無法實作 FnMut。 ``` :::info :bulb: Arc 尚未支援 `dyn Fn()` !!! 看到文件的 [Implementors 部分](https://doc.rust-lang.org/std/ops/trait.Fn.html#implementors) : ```rust= impl<Args, F, A> Fn<Args> for Box<F, A> ``` 分析未支援 Arc 的原因 : Arc 支援 Unsized 的值。Arc 可以支持作為寬指標,因此它可以容納 `dyn Fn()`,如果它可以容納 `dyn Fn()`,並且能夠給你 closure state 的共享參考,那麼它應該能夠實作 `Fn`,因為這只需要能夠獲取 closure state 的共享參考。這表明這裡可能存在某種實作缺失,這可能是由於我們正在查看的關於 Unsized Rvalues 的問題有關,也許 Unsized Rvalues 被專門用來處理 `Box`。 ::: ## const Fn [0:49:44](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=2984s) 接著看到 `const Fn` : ```rust= fn main() { let x = || 0; // constant closure // 你可以在編譯時期求值這個 closure foo(x); } const fn make_zero() -> i32 // 類似 Line 3 { 0 } const fn foo<F: FnOnce()>(f: F) { f() // 目前不行, // 因為編譯器不知道 f 可以當 constant 呼叫。 } ``` 今天在穩定版本中,Line 15 的問題,目前無能為力。但有一個有趣的 pre-RFC 討論一直在進行,討論關於是否有一種方式可以說,我想要對任何實作此 trait 的型別進行泛型化,但僅使用可 constant 求值的事物。 Jon 目前想到的語法是 (1.61.0 之前可以編譯) : ```diff= +#![feature(const_trait_impl, const_fn_trait_bound)] fn main() { let x = || 0; foo(x); } const fn make_zero() -> i32 // 類似 Line 3 { 0 } -const fn foo<F: FnOnce()>(f: F) +const fn foo<F: ~const FnOnce()>(f: F) { f() } ``` * `const` : `FnOnce()` 必須為 constant。 * `~const` : 如果 `FnOnce()` 是 constant,`foo()` 才會是 constant,反之非然。 * `?const` : 表 `FnOnce()` 可能是或不是 constant。 ```rust= $ rustup override set nightly $ cargo run ``` 比較主程式與非主程式傳入的 closure 皆為非 const 的情形 : ```rust= #![feature(const_trait_impl, const_fn_trait_bound)] fn main() { let x = || { String::from("new"); // x 是 !const -> foo 不是 const }; foo(x); // main 不是 const,所以沒要求 foo 是 const, // 所以 main 可以呼叫 foo。 } const fn test_foo() -> i32 { let x = || { String::from("new"); // x 是 !const -> foo 不是 const }; foo(x); // 在非 main 的地方呼叫 foo,要求 foo 是 const, // 所以 test_foo 不可以呼叫 foo。 } // ~const FnOnce() 在 Jon 測的當下,功能尚未穩定, // 所以 Line 14 ~ Line 24 Jon 沒有測出他想要展示的東西。 +const fn foo<F: ~const FnOnce()>(f: F) { f() } ``` ## for bounds [1:00:28](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=3628s) Q : Sometimes you end up with complicated lifetime bounds with for are. A : 如下 : ```rust= fn main() { quox(|x| x); } fn quox<F>(f: F) where F: Fn(&str) -> &str { } ``` 上面程式碼如預期運作,但有些情況下會變得更加複雜。比如在 bound 中,通常(雖然不總是)必須指定生命周期。在這裡,生命周期是什麼?通常情況下,當某物回傳時,你通常可以省略生命周期,但如果我們要嘗試指定這裡的生命周期,它們是什麼?因為這裡沒有生命周期。如果我們嘗試為這個 trait bound 填入生命周期,實際上是什麼呢? Line 5 沒有標記 `'a`,因為我們不能在這裡指派 `'a`。我們真正想說的是,我們希望 `F` 有生命周期,在輸出中重用其得到的生命周期。我們想說的是,它被允許參考與其輸入中參考的相同的東西。 特殊的 for 語法 : ```rust= fn main() { quox(|x| x); } fn quox<F>(f: F) where F: for<'a>Fn(&'a str) -> &'a str { } ``` `F: for<'a>Fn(&'a str) -> &'a str` 的實際意義是對 `F: Fn(&str) -> &str` 的 de-sugar。你可以把這個理解為對於任何生命週期標記 `'a`,`F` 是一個從生命週期 `'a` 的 `&str` 到另一個具有相同生命週期 `'a` 的 `&str` 的函式實作,所以其實並不那麼複雜。 這是一種方式說明它需要對於任何生命週期都成立,這才是應該有的 bound 。實際上,你幾乎不需要給出這樣的 `for` bound,但有時如果你的 trait bound 具有生命週期但不是 `Fn`,則可能會發生這種情況。對於任何 `Fn`、`FnMut` 或 `FnOnce` trait 的東西,編譯器通常很擅長推斷這一點,但一旦你在這裡開始擁有其他不是 `Fn` 的 traits,有時就需要使用 `for`,但這應該是非常非常罕見的。 ## closures in async fn [1:04:06](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=3846s) :::warning :question: [1:04:06](https://www.youtube.com/watch?v=dHkzSZnYXmk&t=3846s) Q : If I want to pass a closure to async function the clousure needs to be 'static right? How does this kind of closure capture it's enviroment? A : 如果你想將 closure 傳給 `async fn`,你可以這樣做,沒有什麼能阻止你接受任何函式 : ```rust= async fn quox<F>(f: F) where F: for<'a>Fn(&'a str) -> &'a str { } ``` 它不需要是 static,更多的是通常與 `Future` 一起使用,特別是如果你想要像 `tokio::spawn` 這樣的東西 : ```rust= fn main() { tokio::spawn(quox(|x| x)); // 像 thread::spawn 一樣要求參數是 static } // 如果這裡的 F 不是 static, // 回傳的 Future 也不會是 static。 async fn quox<F>(f: F) where F: for<'a>Fn(&'a str) -> &'a str { } // de-sugar Line 9 - Line 14 // fn quox<F>(f: F) -> impl Future<Output = ()> // where // F: for<'a>Fn(&'a str) -> &'a str // { // async move { // let _ = f; // } // } ``` `impl trait` 就像 `async fn` 一樣,自動 capture 其輸入的生命週期。因此,如果輸入與某個生命週期相關聯,則輸出型別也將與該相同的生命週期相關聯,這意味著它將不是 static,除非輸入是 static。這就是為什麼你通常需要將 static 添加到傳遞給 `async` 函式的泛型中的原因,這不是因為它們是必需的,如以下例子 : ```rust= fn main() { quox(|x| x).await; // 如果在這裡 await,那麼回傳的 Future 不需要是 static。 // 只有在嘗試進行類似於 spawning 的操作時, // 才需要將 Future 的生命週期延長到當前的 stack frame 之外 } ``` ::: Q : you often need to pin it, tho A : 你應該很少需要手動 pin 東西,一般來說,`await` 語法應該就會處理它。 ## 待整理 1. 0:42:42 2. 1:04:06

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully