日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

rust为什么显示不了国服_捋捋 Rust 中的 impl Trait 和 dyn Trait

發(fā)布時(shí)間:2023/12/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rust为什么显示不了国服_捋捋 Rust 中的 impl Trait 和 dyn Trait 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

緣起

一切都要從年末換工作碰上特殊時(shí)期, 在家閑著無(wú)聊又讀了幾首詩(shī), 突然想寫(xiě)一個(gè)可以瀏覽和背誦詩(shī)詞的 TUI 程序說(shuō)起. 我選擇了 Cursive 這個(gè) Rust TUI 庫(kù). 在實(shí)現(xiàn)時(shí)有這么一個(gè)函數(shù), 它會(huì)根據(jù)參數(shù)的不同返回某個(gè)組件(如 Button, TextView 等). 在 Cursive 中, 每個(gè)組件都實(shí)現(xiàn)了 View 這個(gè) trait, 最初這個(gè)函數(shù)只會(huì)返回某個(gè)確定的組件, 所以函數(shù)簽名可以這樣寫(xiě)

fn some_fn(param: SomeType) -> Button

隨著開(kāi)發(fā)進(jìn)度增加, 這個(gè)函數(shù)需要返回 Button, TextView 等組件中的一個(gè), 我下意識(shí)地寫(xiě)出了類似于下面的代碼

fn some_fn(param1: i32, param2: i32) -> impl View {if param1 > param2 {// do something...return Button {};} else {// do something...return TextView {};} }

可惜 Rust 編譯器一如既往地打臉, Rust 編譯器報(bào)錯(cuò)如下

--> srcmain.rs:19:16| 13 | fn some_fn(param1: i32, param2: i32) -> impl View {| --------- expected because this return type... ... 16 | return Button {};| --------- ...is found to be `Button` here ... 19 | return TextView {};| ^^^^^^^^^^^ expected struct `Button`, found struct `TextView`error: aborting due to previous errorFor more information about this error, try `rustc --explain E0308`.

從編譯器報(bào)錯(cuò)信息看函數(shù)返回值雖然是 impl View 但其從 if 分支推斷返回值類型為 Button 就不再接受 else 分支返回的 TextView. 這與 Rust 要求 if else 兩個(gè)分支的返回值類型相同的特性一致. 那能不能讓函數(shù)返回多種類型呢? Rust 之所以要求函數(shù)不能返回多種類型是因?yàn)?Rust 在需要在 編譯期確定返回值占用的內(nèi)存大小, 顯然不同類型的返回值其內(nèi)存大小不一定相同. 既然如此, 把返回值裝箱, 返回一個(gè)胖指針, 這樣我們的返回值大小可以確定了, 這樣也許就可以了吧. 嘗試把函數(shù)修改成如下形式:

fn some_fn(param1: i32, param2: i32) -> Box<View> {if param1 > param2 {// do something...return Box::new(Button {});} else {// do something...return Box::new(TextView {});} }

現(xiàn)在代碼通過(guò)編譯了, 但如果使用 Rust 2018, 你會(huì)發(fā)現(xiàn)編譯器會(huì)拋出警告:

warning: trait objects without an explicit `dyn` are deprecated--> srcmain.rs:13:45| 13 | fn some_fn(param1: i32, param2: i32) -> Box<View> {| ^^^^ help: use `dyn`: `dyn View`|= note: `#[warn(bare_trait_objects)]` on by default

編譯器告訴我們使用 trait object 時(shí)不使用 dyn 的形式已經(jīng)被廢棄了, 并且還貼心的提示我們把 Box<View> 改成 Box<dyn View>, 按編譯器的提示修改代碼, 此時(shí)代碼 no warning, no error, 完美.

但 impl Trait 和 Box<dyn Trait> 除了允許多種返回值類型的之外還有什么區(qū)別嗎? trait object 又是什么? 為什么 Box<Trait> 形式的返回值會(huì)被廢棄而引入了新的 dyn 關(guān)鍵字呢?

埋坑

impl Trait 和 dyn Trait 在 Rust 分別被稱為靜態(tài)分發(fā)和動(dòng)態(tài)分發(fā). 在第一版的 Rust Book 這樣解釋分發(fā)(dispatch)

When code involves polymorphism, there needs to be a mechanism to determine which specific version is actually run. This is called ‘dispatch’. There are two major forms of dispatch: static dispatch and dynamic dispatch. While Rust favors static dispatch, it also supports dynamic dispatch through a mechanism called ‘trait objects’.

即當(dāng)代碼涉及多態(tài)時(shí), 需要某種機(jī)制決定實(shí)際調(diào)用類型. Rust 的 Trait 可以看作某些具有通過(guò)特性類型的集合, 以上面代碼為例, 在寫(xiě)代碼時(shí)我們不關(guān)心具體類型, 但在編譯或運(yùn)行時(shí)必須確定 Button 還是 TextView. 靜態(tài)分發(fā), 正如靜態(tài)類型語(yǔ)言的"靜態(tài)"一詞說(shuō)明的, 在編譯期就確定了具體調(diào)用類型. Rust 編譯器會(huì)通過(guò)單態(tài)化(Monomorphization) 將泛型函數(shù)展開(kāi).

假設(shè) Foo 和 Bar 都實(shí)現(xiàn)了 Noop 特性, Rust 會(huì)把函數(shù)

fn x(...) -> impl Noop

展開(kāi)為

fn x_for_foo(...) -> Foo fn x_for_bar(...) -> Bar

(僅作原理說(shuō)明, 不保證編譯會(huì)這樣展開(kāi)函數(shù)名).

通過(guò)單態(tài)化, 編譯器消除了泛型, 而且沒(méi)有性能損耗, 這也是 Rust 提倡的形式, 缺點(diǎn)是過(guò)多展開(kāi)可能會(huì)導(dǎo)致編譯生成的二級(jí)制文件體積過(guò)大, 這時(shí)候可能需要重構(gòu)代碼.

靜態(tài)分發(fā)雖然有很高的性能, 但在文章開(kāi)頭其另一個(gè)缺點(diǎn)也有所體現(xiàn), 那就是無(wú)法讓函數(shù)返回多種類型, 因此 Rust 也支持通過(guò) trait object 實(shí)現(xiàn)動(dòng)態(tài)分發(fā). 既然 Trait 是具有某種特性的類型的集合, 那我們可以把 Trait 也看作某種類型, 但它是"抽象的", 就像 OOP 中的抽象類或基類, 不能直接實(shí)例化.

Rust 的 trait object 使用了與 c++ 類似的 vtable 實(shí)現(xiàn), trait object 含有1個(gè)指向?qū)嶋H類型的 data 指針, 和一個(gè)指向?qū)嶋H類型實(shí)現(xiàn) trait 函數(shù)的 vtable, 以此實(shí)現(xiàn)動(dòng)態(tài)分發(fā). 更加詳細(xì)的介紹可以在

Exploring Dynamic Dispatch in Rust?alschwalm.com

看到. 既然 trait object 在實(shí)現(xiàn)時(shí)可以確定大小, 那為什么不用 fn x() -> Trait 的形式呢? 雖然 trait object 在實(shí)現(xiàn)上可以確定大小, 但在邏輯上, 因?yàn)?Trait 代表類型的集合, 其大小無(wú)法確定. 允許 fn x() -> Trait 會(huì)導(dǎo)致語(yǔ)義上的不和諧. 那 fn x() -> &Trait 呢? 當(dāng)然可以! 但鑒于這種場(chǎng)景下都是在函數(shù)中創(chuàng)建然后返回該值的引用, 顯然需要加上生命周期:

fn some_fn(param1: i32, param2: i32) -> &'static View {if param1 > param2 {// do something...return &Button {};} else {// do something...return &TextView {};} }

我不喜歡添加額外的生命周期說(shuō)明, 想必各位也一樣. 所以我們可以用擁有所有權(quán)的 Box 智能指針避免煩人的生命周期說(shuō)明. 至此 Box<Trait> 終于出現(xiàn)了. 那么問(wèn)題來(lái)了, 為什么編譯器會(huì)提示 Box<Trait> 會(huì)被廢棄, 特地引入了 dyn 關(guān)鍵字呢? 答案可以在 RFC-2113 中找到.

RFC-2113 明確說(shuō)明了引入 dyn 的原因, 即語(yǔ)義模糊, 令人困惑, 原因在于沒(méi)有 dyn 讓 Trait 和 trait objects 看起來(lái)完全一樣, RFC 列舉了3個(gè)例子說(shuō)明.

第一個(gè)例子, 加入你看到下面的代碼, 你知道作者要干什么嗎?

impl SomeTrait for AnotherTrait impl<T> SomeTrait for T where T: Another

你看懂了嗎? 說(shuō)實(shí)話我也看不懂 : ) PASS

第二個(gè)例子, impl MyTrait {} 是正確的語(yǔ)法, 不過(guò)這樣會(huì)讓人以為這會(huì)在 Trait 上添加默認(rèn)實(shí)現(xiàn), 擴(kuò)展方法或其他 Trait 自身的一些操作. 實(shí)際上這是在 trait object 上添加方法.

如在下面代碼說(shuō)明的, Trait 默認(rèn)實(shí)現(xiàn)的正確定義方法是在定義 Trait 時(shí)指定, 而不應(yīng)該在 impl Trait {} 語(yǔ)句塊中.

trait Foo {fn default_impl(&self) {println!("correct impl!");} }impl Foo {fn trait_object() {println!("trait object impl");} }struct Bar {}impl Foo for Bar {}fn main() {let b = Bar{};b.default_impl();// b.trait_object();Foo::trait_object(); }

Bar 在實(shí)現(xiàn)了 Foo 后可以通過(guò) b.default_impl 調(diào)用, 無(wú)需額外實(shí)現(xiàn), 但 b.trait_object 則不行, 因?yàn)?trait_object 方法是 Foo 的 trait object 上的方法.

如果是 Rust 2018 編譯器應(yīng)該還會(huì)顯示一條警告, 告訴我們應(yīng)該使用 impl dyn Foo {}

第三個(gè)例子則以函數(shù)類型和函數(shù) trait 作對(duì)比, 兩者差別只在于首字母是否大寫(xiě)(Fn代表函數(shù)trait object, fn則是函數(shù)類型), 難免會(huì)把兩者弄混.

更加詳細(xì)的說(shuō)明可以移步

RFC-2113?github.com

.

總結(jié)

impl trait 和 dyn trait 區(qū)別在于靜態(tài)分發(fā)于動(dòng)態(tài)分發(fā), 靜態(tài)分發(fā)性能 好, 但大量使用有可能造成二進(jìn)制文件膨脹; 動(dòng)態(tài)分發(fā)以 trait object 的概念通過(guò)虛表實(shí)現(xiàn), 會(huì)帶來(lái)一些運(yùn)行時(shí)開(kāi)銷. 又因 trait object 與 Trait 在不引入 dyn 的情況下經(jīng)常導(dǎo)致語(yǔ)義混淆, 所以 Rust 特地引入 dyn 關(guān)鍵字, 在 Rust 2018 中已經(jīng)穩(wěn)定.

引用

以下是本文參考的資料

impl Trait for returning complex types with ease?doc.rust-lang.orgimpl trait 社區(qū)跟蹤?github.comrust-lang/rfcs?github.comTraits and Trait Objects in Rust?joshleeb.comDynamic vs. Static Dispatch?lukasatkinson.deExploring Dynamic Dispatch in Rust?alschwalm.com

PS: 題圖為盧浦大橋, 全上海我最喜歡的大橋, 沒(méi)有之一~

總結(jié)

以上是生活随笔為你收集整理的rust为什么显示不了国服_捋捋 Rust 中的 impl Trait 和 dyn Trait的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。