Rust閉包的蟲洞穿梭

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 閉包是什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"閉包(Closure)的概念由來已久。無論哪種語言,閉包的概念都被以下幾個特徵共同約束:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"匿名函數"},{"type":"text","text":"(非獨有,函數指針也可以);"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"可以調用閉包,並顯式傳遞參數"},{"type":"text","text":"(非獨有,函數指針也可以);"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"以變量形式存在,可以傳來傳去"},{"type":"text","text":"(非獨有,函數指針也可以);"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"可以在閉包內直接捕獲並使用定義所處作用域的值(獨有)"},{"type":"text","text":";"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"神奇的是最後一點,理解起來也比較彆扭的,習慣就好了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了說明上述特徵,可以看一個Rust例子。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"fn display(age: u32, print_info: T)\n where T: Fn(u32)\n{\n print_info(age);\n}\n\nfn main() {\n let name = String::from(\"Ethan\");\n\n let print_info_closure = |age|{\n println!(\"name is {}\", name);\n println!(\"age is {}\", age);\n };\n\n let age = 18;\n display(age, print_info_closure);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"name is Ethan"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"age is 18"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,閉包作爲匿名函數存在了"},{"type":"codeinline","content":[{"type":"text","text":"print_info_closure"}]},{"type":"text","text":"棧變量中,然後傳遞給了函數"},{"type":"codeinline","content":[{"type":"text","text":"display"}]},{"type":"text","text":"作爲參數,在"},{"type":"codeinline","content":[{"type":"text","text":"display"}]},{"type":"text","text":"內部調用了閉包,並傳遞了參數"},{"type":"codeinline","content":[{"type":"text","text":"age"}]},{"type":"text","text":"。最後神奇的事情出現了:在函數"},{"type":"codeinline","content":[{"type":"text","text":"display"}]},{"type":"text","text":"中調用的閉包居然打印出了函數"},{"type":"codeinline","content":[{"type":"text","text":"main"}]},{"type":"text","text":"作用域中的變量"},{"type":"codeinline","content":[{"type":"text","text":"name"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b5/b5d5b71e8b41eec3249e9515b26df9e2.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"閉包的精髓,就在於它同時涉及兩個作用域"},{"type":"text","text":",就彷彿打開了一個\"蟲洞\",讓不同作用域的變量穿梭其中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"let x_closure = ||{};"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單獨一行代碼,就藏着這個奧妙:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"賦值"},{"type":"codeinline","content":[{"type":"text","text":"="}]},{"type":"text","text":"的左側,是存儲閉包的變量,它處在一個作用域中,也就是我們說的閉包"},{"type":"text","marks":[{"type":"strong"}],"text":"定義處"},{"type":"text","text":"的環境上下文;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"賦值"},{"type":"codeinline","content":[{"type":"text","text":"="}]},{"type":"text","text":"的右側,那對花括號"},{"type":"codeinline","content":[{"type":"text","text":"{}"}]},{"type":"text","text":"裏,也是一個作用域,它在閉包"},{"type":"text","marks":[{"type":"strong"}],"text":"被調用處"},{"type":"text","text":"動態產生;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論左側右側,都定義了閉包的屬性,天然的聯通了兩個作用域。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於閉包,Rust如此,其他語言也大抵如此。不過,Rust不是還有所有權、生命週期這一檔子事兒麼,所以還可以深入分析下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. Rust閉包捕獲上下文的方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如本篇題目,Rust閉包如何捕獲上下文?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"換個問法,"},{"type":"codeinline","content":[{"type":"text","text":"main"}]},{"type":"text","text":"作用域中的變量"},{"type":"codeinline","content":[{"type":"text","text":"name"}]},{"type":"text","text":"是以何種方式進入閉包的作用域的(第1節例子)?轉移or借用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"It Depends,視情況而定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Rust在std中定義了3種trait:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"FnOnce:閉包內對外部變量存在轉移操作,導致外部變量不可用(所以只能call一次)"},{"type":"text","text":";"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"FnMut:閉包內對外部變量直接使用,並進行修改"},{"type":"text","text":";"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Fn:閉包內對外部變量直接使用,不進行修改"},{"type":"text","text":";"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後者能辦到的,前者一定能辦到。反之則不然。所以,編譯器對閉包簽名進行推理時:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現FnMut的,同時也實現了FnOnce;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現Fn的,同時也實現了FnMut和FnOnce。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第1節的例子,將"},{"type":"codeinline","content":[{"type":"text","text":"display"}]},{"type":"text","text":"的泛型參數從Fn改成FnMut,也可以無警告通過。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"fn display(age: u32, mut print_info: T)\n where T: FnMut(u32)\n{\n print_info(age);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對環境變量進行捕獲的閉包,需要額外的空間支持才能將環境變量進行存儲。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 作爲參數的閉包簽名"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面代碼"},{"type":"codeinline","content":[{"type":"text","text":"display"}]},{"type":"text","text":"函數定義,要接受一個閉包作爲參數,揭示瞭如何顯式的描述閉包的簽名:在泛型參數上添加trait約束,比如"},{"type":"codeinline","content":[{"type":"text","text":"T: FnMut(u32)"}]},{"type":"text","text":",其中"},{"type":"codeinline","content":[{"type":"text","text":"(u32)"}]},{"type":"text","text":"顯式的表示了輸入參數的類型。"},{"type":"text","marks":[{"type":"strong"}],"text":"儘管是泛型參數約束,但是函數簽名(除了沒有函數名)描述還是非常精確的"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"順便說一句,Rust的泛型真的是幹了不少事情,除了泛型該乾的,還能添加trait約束,還能描述生命週期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"描述簽名是一回事,但是誰來定義閉包的簽名呢?閉包定義處,我們沒有看到任何的類型約束,直接就可以調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是:"},{"type":"text","marks":[{"type":"strong"}],"text":"閉包的簽名,編譯器全部一手包辦了,它會將首次調用閉包傳入參數和返回值的類型,綁定到閉包的簽名"},{"type":"text","text":"。這就意味着,一旦閉包被調用過一次後,再次調用閉包時傳入的參數類型,就必須是和第一次相同。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"傳入參數和返回值類型綁定好了,但你心中難免還會有一絲憂愁:描述生命週期的泛型參數腫麼辦?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Rust編譯器也搞得定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"fn main(){\n\t\tlet lifttime_closure = |a, b|{\n println!(\"{}\", a);\n println!(\"{}\", b);\n b\n };\n let a = String::from(\"abc\");\n let c;\n {\n let b = String::from(\"xyz\");\n c = lifttime_closure(&a, &b);\n }\n println!(\"{}\", c);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上代碼無法通過編譯,成功檢測出了懸垂引用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"error[E0597]: "},{"type":"codeinline","content":[{"type":"text","text":"b"}]},{"type":"text","text":" does not live long enough"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"顯然,對於閉包,編譯期可以對引用的生命週期進行檢查,以保證引用始終有效。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個例子,與其解釋閉包與函數的區別,不如解釋匿名函數與具名函數的區別:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具名函數是簽名在先的,對於編譯器來說,調用方和函數內部實現,只要分別遵守簽名的約定即可。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"匿名函數的簽名則是被推理出來的,編譯器要"},{"type":"text","marks":[{"type":"strong"}],"text":"看全看透"},{"type":"text","text":"調用方的實際輸入,以及函數內部的實際返回,檢查自然也就順帶做掉了。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. 函數返回閉包"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第1節的例子,我們將一個閉包作爲函數參數傳入,那麼根據閉包的特性,它應該能夠作爲函數的返回值。答案是肯定的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於前面介紹的Fn trait,我們定義一個返回閉包的函數,代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"fn closure_return() -> Fn() -> (){\n\t||{} \n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可是,編譯失敗了:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"error[E0746]: return type cannot have an unboxed trait object"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"doesn't have a size known at compile-time"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"失敗信息顯示,編譯器無法確定函數返回值的大小。一個閉包有多大呢?並不重要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開門見山,通用的解決方法是:爲了能夠返回閉包,可以使用一次裝箱,從而將棧內存變量裝箱存入堆內存,這樣無論閉包有多大,函數返回值都是一個確定大小的指針。下面的代碼裏,使用"},{"type":"codeinline","content":[{"type":"text","text":"Box::new"}]},{"type":"text","text":"即可完成裝箱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"fn closure_inside() -> Box ()>\n{\n let mut age = 1;\n let mut name = String::from(\"Ethan\");\n\n let age_closure = move || {\n name.push_str(\" Yuan\");\n age += 1;\n println!(\"name is {}\", name);\n println!(\"age is {}\", age);\n };\n\n Box::new(age_closure)\n}\n\nfn main(){\n let mut age_closure = closure_inside();\n age_closure();\n age_closure();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行結果如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"name is Ethan Yuan"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"age is 2"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"name is Ethan Yuan Yuan"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"age is 3"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的代碼,除了讓函數成功返回閉包之外,還有一個目的,我們想讓閉包捕獲函數內部環境中的值,但這次有些不同:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第1節代碼示例,我們把外層的環境上下文,通過將閉包傳入內層函數,這個不難理解,因爲外層變量的生命週期更長,內層函數訪問時,外層變量還活着;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而本節代碼所做的,是通過閉包將內層函數的環境變量傳出來給外層環境;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內層函數調用完成後就會銷燬內層環境變量,那如何做到呢?幸好,Rust有"},{"type":"text","marks":[{"type":"strong"}],"text":"所有權轉移"},{"type":"text","text":"。只要能促成內層函數的環境變量向閉包進行所有權的轉移,這個操作順理成章。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正因爲Rust具有所有權轉移的概念,返回閉包(同時捕獲環境變量)的機理,Rust的要比任何具有垃圾回收語言(JavaScript、Java、C#)的解釋都更簡單明瞭。後者總會給人一絲不安:內部函數調用都結束了,居然局部變量還活着。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼中的所有權轉移,這裏使用了關鍵字"},{"type":"codeinline","content":[{"type":"text","text":"move"}]},{"type":"text","text":",它可以在構建閉包時,強制將要捕獲變量的所有權轉移至閉包內部的特別存儲區。需要注意的是,使用"},{"type":"codeinline","content":[{"type":"text","text":"move"}]},{"type":"text","text":",並不影響閉包的"},{"type":"codeinline","content":[{"type":"text","text":"trait"}]},{"type":"text","text":",本例中可以看到閉包是"},{"type":"codeinline","content":[{"type":"text","text":"FnMut"}]},{"type":"text","text":",而不是"},{"type":"codeinline","content":[{"type":"text","text":"FnOnce"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章