国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Fn FnMut FnOnce 傻傻分不清

開發 前端
同時閉包引用變量也是有優先級的:優先只讀借用,然后可變借用,最后轉移所有權。本篇文章看下,如何將閉包當成參數或返回值。

[[415256]]

本文轉載自微信公眾號「董澤潤的技術筆記」,作者董澤潤。轉載本文請聯系董澤潤的技術筆記公眾號。

同時閉包引用變量也是有優先級的:優先只讀借用,然后可變借用,最后轉移所有權。本篇文章看下,如何將閉包當成參數或返回值

Go 閉包調用

  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. func test(f func()) { 
  6.     f() 
  7.     f() 
  8.  
  9. func main() { 
  10.     a:=1 
  11.     fn := func() { 
  12.         a++ 
  13.         fmt.Printf("a is %d\n", a) 
  14.     } 
  15.     test(fn) 

上面是 go 的閉包調用,我們把 fn 當成參數,傳給函數 test. 閉包捕獲變量 a, 做自增操作,同時函數 fn 可以調用多次

對于熟悉 go 的人來說,這是非常自然的,但是換成 rust 就有問題了

  1. fn main() { 
  2.     let s = String::from("wocao"); 
  3.     let f = || {println!("{}", s);}; 
  4.     f(); 

比如上面這段 rust 代碼,我如果想把閉包 f 當成參數該怎么寫呢?上周分享的閉包我們知道,閉包是匿名的

  1. c = hello_cargo::main::closure-2 (0x7fffffffe0e0, 0x7fffffffe0e4) 
  2. b = hello_cargo::main::closure-1 (0x7fffffffe0e0) 
  3. a = hello_cargo::main::closure-0 

在運行時,類似于上面的結構體,閉包結構體命名規則 closure-xxx, 同時我們是不知道函數簽名的

引出 Trait

官方文檔 給出了方案,標準庫提供了幾個內置的 trait, 一個閉包一定實現了 Fn, FnMut, FnOnce 其中一個,然后我們可以用泛型 + trait 的方式調用閉包

  1. $ cat src/main.rs 
  2. fn test<T>(f: T) where 
  3.     T: Fn() 
  4.     f(); 
  5.  
  6. fn main() { 
  7.     let s = String::from("董澤潤的技術筆記"); 
  8.     let f = || {println!("{}", s);}; 
  9.     test(f); 
  10.  
  11. $ cargo run 
  12.     Finished dev [unoptimized + debuginfo] target(s) in 0.00s 
  13.      Running `target/debug/hello_cargo` 
  14. 董澤潤的技術筆記 

上面將閉包 f 以泛型參數的形式傳給了函數 test, 因為閉包實現了 Fn trait. 剛學這塊的人可能會糊涂,其實可以理解類比 go interface, 但本質還是不一樣的

  1. let f = || {s.push_str("不錯");}; 

假如 test 聲明不變,我們的閉包修改了捕獲的變量呢?

  1. |     let f = || {s.push_str("不錯");}; 
  2. |             ^^  - closure is `FnMut` because it mutates the variable `s` here 
  3. |             | 
  4. |             this closure implements `FnMut`, not `Fn` 
  5. |     test(f); 

報錯說 closure 實現的 trait 是 FnMut, 而不是 Fn

  1. fn test<T>(mut f: T) where 
  2.     T: FnMut() 
  3.     f(); 
  4.  
  5. fn main() { 
  6.     let mut s = String::from("董澤潤的技術筆記"); 
  7.     let f = || {s.push_str("不錯");}; 
  8.     test(f); 

上面是可變借用的場景,我們再看一下 move 所有權的情況

  1. fn test<T>(f: T) where 
  2.     T: FnOnce() 
  3.     f(); 
  4.  
  5. fn main() { 
  6.     let s = String::from("董澤潤的技術筆記"); 
  7.     let f = || {let _ = s;}; 
  8.     test(f); 

上面我們把自由變量 s 的所有權 move 到了閉包里,此時 T 泛型的特征變成了 FnOnce, 表示只能執行一次。那如果 test 調用閉包兩次呢?

  1. 1 | fn test<T>(f: T) where 
  2.   |            - move occurs because `f` has type `T`, which does not implement the `Copy` trait 
  3. ... 
  4. 4 |     f(); 
  5.   |     --- `f` moved due to this call 
  6. 5 |     f(); 
  7.   |     ^ value used here after move 
  8.   | 
  9. note: this value implements `FnOnce`, which causes it to be moved when called 
  10.  --> src/main.rs:4:5 
  11.   | 
  12. 4 |     f(); 

編譯器提示第一次調用的時候,己經 move 了,再次調用無法訪問。很明顯此時自由變量己經被析構了 let _ = s; 離開詞法作用域就釋放了,rust 為了內存安全當然不允許繼續訪問

  1. fn test<T>(f: T) where 
  2.     T: Fn() 
  3.     f(); 
  4.     f(); 
  5.  
  6. fn main() { 
  7.     let s = String::from("董澤潤的技術筆記"); 
  8.     let f = move || {println!("s is {}", s);}; 
  9.     test(f); 
  10.     //println!("{}", s); 

那么上面的代碼例子, 是否可以運行呢?當然啦,此時變量 s 的所有權 move 給了閉包 f, 生命周期同閉包,反復調用也沒有副作用

深入理解

本質上 Rust 為了內存安全,才引入這么麻煩的處理。平時寫 go 程序,誰會在乎對象是何時釋放,對象是否存在讀寫沖突呢?總得有人來做這個事情,Rust 選擇在編譯期做檢查

  • FnOnce consumes the variables it captures from its enclosing scope, known as the closure’s environment. To consume the captured variables, the closure must take ownership of these variables and move them into the closure when it is defined. The Once part of the name represents the fact that the closure can’t take ownership of the same variables more than once, so it can be called only once.
  • FnMut can change the environment because it mutably borrows values.
  • Fn borrows values from the environment immutably.

上面來自官網的解釋,Fn 代表不可變借用的閉包,可重復執行,FnMut 代表閉包可變引用修改了變量,可重復執行 FnOnce 代表轉移了所有權,同時只能執行一次,再執行的話自由變量脫離作用域回收了

  1. # mod foo { 
  2. pub trait Fn<Args> : FnMut<Args> { 
  3.     extern "rust-call" fn call(&self, args: Args) -> Self::Output
  4.  
  5. pub trait FnMut<Args> : FnOnce<Args> { 
  6.     extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output
  7.  
  8. pub trait FnOnce<Args> { 
  9.     type Output
  10.  
  11.     extern "rust-call" fn call_once(self, args: Args) -> Self::Output
  12. # } 

上面是標準庫中,Fn, FnMut, FnOnce 的實現。可以看到 Fn 繼承自 FnMut, FnMut 繼承自 FnOnce

  1. Fn(u32) -> u32 

前文例子都是無參數的,其實還可以帶上參數

由于 Fn 是繼承自 FnMut, 那么我們把實現 Fn 的閉包傳給 FnMut 的泛型可以嘛?

  1. $ cat src/main.rs 
  2. fn test<T>(mut f: T) where 
  3.     T: FnMut() 
  4.     f(); 
  5.     f(); 
  6.  
  7. fn main() { 
  8.     let s = String::from("董澤潤的技術筆記"); 
  9.     let f = || {println!("s is {}", s);}; 
  10.     test(f); 
  1. $ cargo run 
  2.    Compiling hello_cargo v0.1.0 (/Users/zerun.dong/code/rusttest/hello_cargo) 
  3.     Finished dev [unoptimized + debuginfo] target(s) in 1.47s 
  4.      Running `target/debug/hello_cargo` 
  5. is 董澤潤的技術筆記 
  6. is 董澤潤的技術筆記 

當然可以看起來沒有問題,FnMut 告訴函數 test 這是一個會修改變量的閉包,那么傳進來的閉包不修改當然也沒問題

上圖比較出名,由于有繼承關系,實現 Fn 可用于 FnMut 和 FnOnce 參數,實現 FnMut 可用于 FnOnce 參數

函數指針

  1. fn call(f: fn()) {    // function pointer 
  2.     f(); 
  3.  
  4. fn main() { 
  5.     let a = 1; 
  6.  
  7.     let f = || println!("abc");     // anonymous function 
  8.     let c = || println!("{}", &a);  // closure 
  9.  
  10.     call(f); 
  11.     call(c); 

函數和閉包是不同的,上面的例子中 f 是一個匿名函數,而 c 引用了自由變量,所以是閉包。這段代碼是不能執行的

  1. 9  |     let c = || println!("{}", &a);  // closure 
  2.    |             --------------------- the found closure 
  3. ... 
  4. 12 |     call(c); 
  5.    |          ^ expected fn pointer, found closure 

編譯器告訴我們,12 行要求參數是函數指針,不應該是閉包

閉包作為返回值

參考 impl Trait 輕松返回復雜的類型,impl Trait 是指定實現特定特征的未命名但有具體類型的新方法。你可以把它放在兩個地方:參數位置和返回位置

  1. fn returns_closure() -> Box<dyn Fn(i32) -> i32> { 
  2.     Box::new(|x| x + 1) 
  3.  
  4. fn main() { 
  5.     let f = returns_closure(); 
  6.     println!("res is {}", f(11)); 

在以前,從函數處返回閉包的唯一方法是,使用 trait 對象,大家可以試試不用 Box 裝箱的報錯提示

  1. fn returns_closure() -> impl Fn(i32) -> i32 { 
  2.     |x| x + 1 
  3.  
  4. fn main() { 
  5.     let f = returns_closure(); 
  6.     println!("res is {}", f(11)); 

現在我們可以用 impl 來實現閉包的返回值聲明

  1. fn test() -> impl FnMut(char) { 
  2.     let mut s = String::from("董澤潤的技術筆記"); 
  3.     |c| { s.push(c); } 
  4.  
  5. fn main() { 
  6.     let mut c = test(); 
  7.     c('d'); 
  8.     c('e'); 

來看一個和引用生命周期相關的例子,上面的代碼返回閉包 c, 對字符串 s 進行追回作。代碼執行肯定報錯:

  1.  --> src/main.rs:3:5 
  2.   | 
  3. 3 |     |c| { s.push(c); } 
  4.   |     ^^^   - `s` is borrowed here 
  5.   |     | 
  6.   |     may outlive borrowed value `s` 
  7.   | 
  8. note: closure is returned here 
  9.  --> src/main.rs:1:14 
  10.   | 
  11. 1 | fn test() -> impl FnMut(char) { 
  12.   |              ^^^^^^^^^^^^^^^^ 
  13. help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword 
  14.   | 
  15. 3 |     move |c| { s.push(c); } 
  16.   |     ^^^^^^^^ 

提示的很明顯,變量 s 脫離作用域就釋放了,編譯器也提示我們要 move 所有權給閉包,感興趣的自己修改測試一下

 

責任編輯:武曉燕 來源: 董澤潤的技術筆記
相關推薦

2022-05-05 13:54:37

SPI機制APISPI

2021-01-22 08:37:02

二進制Binary SemaReentrant L

2021-10-06 23:31:45

HibernateJPASpring Data

2021-03-23 10:45:23

CookieSession前端

2021-05-09 21:57:08

數據倉庫Olap

2021-03-10 08:56:37

Zookeeper

2021-07-27 07:31:16

JavaArrayList數組

2022-05-15 21:52:04

typeTypeScriptinterface

2021-08-02 12:50:45

sessiontokenJava

2024-02-29 09:08:56

Encoding算法加密

2022-04-15 11:47:47

LVSNginx負載均衡

2019-06-18 09:45:19

正向代理反向代理IT

2020-10-30 08:20:04

SD卡TF卡存儲

2018-12-17 12:30:05

Kubernetes存儲存儲卷

2015-02-28 10:01:00

云計算IT架構虛擬化

2018-05-22 16:24:20

HashMapJavaJDK

2023-02-27 15:46:19

數據元元數據

2025-08-18 03:25:00

2020-03-03 17:35:09

Full GCMinor

2023-09-03 21:18:07

Python編程語言
點贊
收藏

51CTO技術棧公眾號

男人的天堂亚洲一区| 日韩一区二区三区三四区视频在线观看| 亚洲人亚洲人成电影网站色| 夜夜操天天操亚洲| 一本色道久久综合狠狠躁的推荐| 欧美区视频在线观看| 亚洲成av人片在线观看香蕉| 精品粉嫩超白一线天av| 亚洲性生活视频| 欧美激情一级欧美精品| 国产精品久久久久久久9999| 99国产高清| 日韩精品国内| 男人天堂网站在线| 激情五月婷婷久久| 在线免费观看h| 国产一级片在线| free性护士videos欧美| 日韩城人网站| 久久93精品国产91久久综合| 午夜久久tv| 久久精品久久精品| 91丨九色丨尤物| 亚洲国产日韩a在线播放性色| 欧美片在线播放| 中文字幕欧美日韩va免费视频| 欧洲中文字幕国产精品| 精品欧美一区二区三区久久久| 一级黄色片播放| 天堂中文字幕一二区| av免费在线一区二区三区| 日韩大尺度黄色| 欧美猛男男男激情videos| 欧美一级久久| 久久中文字幕电影| 午夜a成v人精品| 日韩av影院在线观看| 欧美亚洲国产精品| 欧美日本亚洲| 欧美丰满熟妇xxxxx| 国内三级在线观看| 欧美成人精品三级网站| 日韩欧美午夜| 激情欧美一区二区| 一区二区三区**美女毛片| 日韩精品一区二区三区蜜臀| 欧美精品激情在线| 久久精品日韩| 妞干网在线免费视频| 97在线观看免费观看高清 | 日韩中文字幕在线播放| 国产精品入口免费视| 一级做a爰片久久| 国产二区三区四区| 成人观看网址| 日韩精品一区二区三区免费观看 | 99精品视频在线观看免费| 欧美日韩在线影院| 最近中文字幕日韩精品 | www国产精品| 久久中文在线| 自拍偷拍欧美激情| 亚洲а∨天堂久久精品9966| 国产97色在线|日韩| 亚洲一卡二卡三卡| 黄网站app在线观看大全免费视频| 天堂在线中文网官网| 国产精品99久久| 成人福利在线看| 欧美日韩免费一区二区三区| 高清欧美性猛交| 亚洲成色www久久网站| 欧美激情图区| 久久人体av| 国产一区成人| 一区二区三区在线观看动漫| 国产午夜精品视频| 国产三级精品在线不卡| 日本成人免费网站| 日本精品不卡| 一本色道88久久加勒比精品| 亚洲欧美日韩一区二区| 在线观看欧美视频| 免费av一区二区三区| 成人在线观看视频app| 欧美97人人模人人爽人人喊视频| 在线观看亚洲| 樱花影视一区二区| xxx一区二区| 亚洲精品中文综合第一页| 日韩电影在线观看完整版| 成人h动漫免费观看网站| 狠狠狠色丁香婷婷综合久久五月| 日本韩国欧美一区二区三区| 国产69久久精品成人看| 国产www免费| 国内老司机av在线| 欧美激情1区| 成人欧美一区二区三区白人| 亚洲男人天堂2019| 国产视频一区二区三区四区| 欧美精品第1页| 欧美日韩视频在线观看一区二区三区| 久久久欧美精品| 91精品国产毛片武则天| 丁香婷婷在线观看| 亚洲另类av| 成人av网址在线| 欧美成人a视频| 亚洲自拍偷拍区| 99riav视频| 国产第一精品| 日韩**一区毛片| 在线观看国产91| 国产精品va在线播放| 美女黄色片视频| 精品国产欧美日韩一区二区三区| 久久中文欧美| 欧美精选午夜久久久乱码6080| 国产主播精品在线| 成人永久免费网站| 1769国产精品视频| 99国产精品一区| 亚洲天堂免费视频| 亚洲精品乱码视频| 久操视频在线免费播放| 久久久久久久久国产一区| 亚洲精品高清在线观看| 欧美激情久久久久| 1024精品视频| 精品123区| 国产乱人伦偷精品视频不卡 | 国产精品高清一区二区| 国产成人在线视频网址| 亚洲国产成人精品一区二区 | 国产情侣一区在线| 国产69精品久久久久777| 亚洲国产三级网| 性欧美.com| 狂野欧美激情性xxxx欧美| 久久亚洲电影| 日韩三级在线免费观看| 黄色91av| 麻豆传媒视频在线| 国产日韩专区| 精品视频在线免费看| 国产99在线免费| 成全电影播放在线观看国语| 欧美不卡在线| 欧美日韩亚洲高清一区二区| 国产一区二区免费在线观看| 黄色av网址在线免费观看| 一区二区三区午夜视频| 欧美综合天天夜夜久久| 国产精品免费观看高清| av色图一区| 噜噜噜躁狠狠躁狠狠精品视频 | 四虎免费av| 偷窥自拍亚洲色图精选| 亚洲男人的天堂一区二区| 国产成人高潮免费观看精品| 午夜av电影| 99久久精品费精品国产风间由美| 狠狠干狠狠久久| 国产精品区一区二区三含羞草| 女女色综合影院| 免费在线观看一区二区三区| 日韩高清不卡av| www.国产在线视频| 亚洲网站免费| 中文字幕一区三区| 国产精品一区二区久久| 污污网站在线| 一本不卡影院| 亚洲国产精品va在线观看黑人| 99亚洲国产精品| av在线成人| 日韩美女视频一区二区| 国产欧亚日韩视频| aaa日本高清在线播放免费观看| 美女黄色成人网| 日韩精品中文字幕在线观看| www.99热这里只有精品| 精品少妇一区| 欧美日韩国内自拍| 久久久久久久有限公司| 国产自产自拍视频在线观看| 99精品偷自拍| 日本精品视频在线观看| 美女欧美视频在线观看免费 | 无码专区aaaaaa免费视频| 国产精品17p| 亚洲1区2区3区视频| 黑人中文字幕一区二区三区| 国产精品电影| 久久久久久久久久美女| 国产精品视频白浆免费视频| 一级毛片视频在线观看| 狠狠久久亚洲欧美| 欧美激情啊啊啊|