吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

實現Rust Trait類型 那么該類型的引用也實現了trait嗎?

jf_wN0SrCdH ? 來源:coding到燈火闌珊 ? 2023-08-28 15:25 ? 次閱讀

如果你在一個類型上實現了一個trait,然后引用了這個類型,那么類型的引用也實現了這個trait嗎?

有一段時間我是這么想的!但實際上并不是,有時候Rust為你做的事情可能會混淆幕后真正發生的事情。

為了演示,讓我從一個名為Speaker的trait和一個實現該trait的空struct開始:

///定義一個trait,有一個speak方法。
traitSpeaker{
fnspeak(&self);
}

/// BasicSpeaker是一個空結構體,只是為了實現Speaker。
structBasicSpeaker;

///BasicSpeakers實現speak方法
implSpeakerforBasicSpeaker{
fnspeak(&self){
println!("Hello!");
}
}
現在,在main函數中,以下代碼應該可以工作:
//創建BasicSpeaker結構體
letspeaker=BasicSpeaker;
//調用在BasicSpeaker上定義的speak方法
speaker.speak();

確實如此,它就會輸出“Hello!”。

如果我引用了一個BasicSpeaker,我仍然可以對它調用speak,因為Rust會自動解除對變量的引用。所以下面的代碼也可以工作:

//定義一個BasicSpeaker的引用
letspeaker_ref:&BasicSpeaker=&speaker;
//通過引用調用在BasicSpeaker上定義的speak方法
speaker_ref.speak();

這可能會讓你認為BasicSpeaker實現了Speaker,引用&BasicSpeaker也實現了Speaker。也許是Rust的魔法?

讓我們更具體地測試一下,定義一個接受impl Speaker類型形參的函數。

fnspeak_to(s:implSpeaker){
s.speak();
}

fnmain(){
//創建BasicSpeaker結構體
letspeaker=BasicSpeaker;
//將speaker傳遞給新函數
speak_to(speaker);
}

這是可行的,因為BasicSpeaker實現了Speaker特性。

讓我們嘗試同樣的事情,但這次是傳遞BasicSpeaker的引用:

//定義一個BasicSpeaker的引用
letspeaker_ref:&BasicSpeaker=&speaker;
//將引用傳遞給'speak_to'
speak_to(speaker_ref);
這行不通!錯誤信息如下所示:
error[E0277]:thetraitbound`&BasicSpeaker:Speaker`isnotsatisfied
-->src/main.rs:31:14
|
31|speak_to(speaker_ref);
|--------^^^^^^^^^^^thetrait`Speaker`isnotimplementedfor`&BasicSpeaker`
||
|requiredbyaboundintroducedbythiscall
|
=help:thetrait`Speaker`isimplementedfor`BasicSpeaker`
note:requiredbyaboundin`speak_to`
-->src/main.rs:16:21
|
16|fnspeak_to(s:implSpeaker){
|^^^^^^^requiredbythisboundin`speak_to`

Formoreinformationaboutthiserror,try`rustc--explainE0277`.

最初的錯誤消息是模糊的,但是第一個代碼塊旁邊的消息更清晰:“&BasicSpeaker沒有實現trait Speaker”。

前面的代碼示例演示了你可以在引用上調用沒有在該引用上實現的方法,因為Rust會默默地為你解引用該值。Rust是這樣做的:

//Rust將'speaker_ref.speak()'轉換為
(*speaker_ref).speak();

這并不意味著&BasicSpeaker(一個引用)實現了Speaker。

直接的解決方案

最直接的解決方案是在BasicSpeaker的引用上實現Speaker,如下所示:

implSpeakerfor&BasicSpeaker{
fnspeak(&self){
println!("Hello!");
}
}
將其添加到代碼中后,就可以編譯和運行了。所以這是一種解決方案,但這并不理想。首先,這基本上是先前實現的重復代碼。下面是一個稍微改進的實現,它只調用底層結構體的方法:
implSpeakerfor&BasicSpeaker{
fnspeak(&self){
return(**self).speak();
}
}

很明顯,我必須對self進行兩次解引用,因為該函數接受&self,而self是&BasicSpeaker。這意味著參數是一個&&BasicSpeaker,必須對其進行兩次解引用才能獲得實現speak()的BasicSpeaker。

好了,現在沒有那么多代碼復制了,但是還有另一個問題,如果我想定義另一個Speaker,比如NamedSpeaker,那么我必須編寫兩次代碼——一次為NamedSpeaker,一次為&NamedSpeaker。

用泛型Trait解決這個問題

我可以寫一個泛型的實現:“如果一個struct T實現了Speaker,那么寫一個用于&T的Speaker實現。”

///所有實現Speaker的事物的引用也必須是Speaker的。
implSpeakerfor&T
where
T:Speaker,
{
fnspeak(&self){
return(**self).speak();
}
}
或者,如果你喜歡,你可以使用下面的,稍微短一點的語法,意思是一樣的:
implSpeakerfor&T{
fnspeak(&self){
return(**self).speak();
}
}
即使我現在已經為&BasicSpeaker編寫了Speaker的實現,但這并不適用于&mut BasicSpeaker!所以這行不通:
//獲取一個對BasicSpeaker的可變引用
letspeaker_mut_ref:&mutBasicSpeaker=&mutspeaker;
//通過可變引用,調用在BasicSpeaker上定義的speak方法
speak_to(speaker_mut_ref);
這需要另一個泛型實現:
///所有實現Speaker的事物的可變引用也必須是Speaker的。
implSpeakerfor&mutT
where
T:Speaker,
{
fnspeak(&self){
return(**self).speak();
}
}
為了完整起見,這里對于Box也是一樣的,當你想把Speaker實現放到堆上時:
implSpeakerforBox
where
T:Speaker,
{
fnspeak(&self){
return(**self).speak();
}
}
一旦添加了這些覆蓋實現,就意味著Speaker的任何新類型(該類型本身、對該類型的任何引用以及包含該類型的任何Box)都自動實現了Speaker trait。

總結

因為Rust會自動解除對trait的引用,它看起來就像引用本身也實現了trait。但事實并非如此。幸運的是,在許多情況下,你可以使用一些泛型trait來修復這個問題。

如果你的trait接口允許,你應該為&T, &mut T和Box提供trait實現,這樣你就可以將這些類型傳遞給任何接受trait實現的函數。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • rust語言
    +關注

    關注

    0

    文章

    57

    瀏覽量

    3029

原文標題:實現了Rust Trait的類型,那么該類型的引用也實現了trait嗎?

文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    基于Rust語言Hash特征的基礎用法和進階用法

    Rust語言是一種系統級編程語言,具有高性能、安全、并發等特點,是近年來備受關注的新興編程語言。在Rust語言中,Hash是一種常用的數據結構,用于存儲鍵值對。Rust語言提供一系列
    的頭像 發表于 09-19 16:02 ?1542次閱讀

    Rust語言中的反射機制

    Rust語言的反射機制指的是在程序運行時獲取類型信息、變量信息等的能力。Rust語言中的反射機制主要通過 Any 實現。 std::any::Any
    的頭像 發表于 09-19 16:11 ?2545次閱讀

    Rust中的From和Into trait的基礎使用方法和進階用法

    、可靠和安全的系統級應用的首選語言。 Rust中的From和Into是兩個重要的trait,它們可以幫助我們進行類型轉換。From trait允許我們從一個
    的頭像 發表于 09-20 10:55 ?1874次閱讀

    “布爾2”的引用 和“Bool引用句柄”為什么不是同一個數據類型

    請教 改變了 按鈕的機械方式, 右圖中“布爾2”的引用 和“Bool引用句柄”為什么不是同一個數據類型
    發表于 04-04 23:21

    子vi如何使其變為非嚴格類型

    請教各位大神一個問題,在一個vi中調用子vi時,通過“右鍵>選擇服務器類型>瀏覽”方式選擇一個子vi后其為嚴格類型,要想實現引用還得使用下
    發表于 05-04 20:29

    Windows -編程-數據類型

    Windows -編程-數據類型Rust 中的每個值都有特定的數據類型,它告訴 Rust 指定什么樣的數據,以便它知道如何處理這些數據。我
    發表于 08-24 14:30

    引用數據類型的概念_引用數據類型有哪幾種

    引用類型類型的實際值引用(類似于指針)表示的數據類型。如果為某個變量分配一個引用
    發表于 11-17 18:29 ?2.6w次閱讀
    <b class='flag-5'>引用</b>數據<b class='flag-5'>類型</b>的概念_<b class='flag-5'>引用</b>數據<b class='flag-5'>類型</b>有哪幾種

    C#良好兼容類型/引用類型

    反觀歷史,C#曾經因為 值類型/引用類型 保守詬病,“拆箱”和“裝箱”一直是個招黑的設計。但后來我們看到,隨著泛型的成熟和普及,隨著泛型容器代替通用容器,裝箱和拆箱的問題已經在很大程
    的頭像 發表于 11-20 10:14 ?1731次閱讀
    C#良好兼容<b class='flag-5'>了</b>值<b class='flag-5'>類型</b>/<b class='flag-5'>引用</b><b class='flag-5'>類型</b>

    Rust原子類型和內存排序

    原子類型在構建無鎖數據結構,跨線程共享數據,線程間同步等多線程并發編程場景中起到至關重要的作用。本文將從Rust提供的原子類型和原子類型的內存排序問題兩方面來介紹。
    的頭像 發表于 10-31 09:21 ?999次閱讀

    Rust中GAT和高階類型

    Rust類型系統級別上與Haskell,Scala有許多相似之處。
    的頭像 發表于 11-07 10:21 ?1234次閱讀

    trait中使用 `async fn`

    { ... } } 更多請看官方blog:https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html 社區受
    的頭像 發表于 11-23 15:40 ?836次閱讀

    重點講解Send與Sync相關的并發知識

    Send與Sync在Rust中屬于marker trait,代碼位于marker.rs,在標記模塊中還有Copy、Unpin等trait
    的頭像 發表于 01-16 09:54 ?1006次閱讀

    為什么Borrow和BorrowMut被定義為泛型trait呢?

    Borrow和BorrowMut traitRust標準庫std:borrow 模塊中用于處理借用數據的trait,通過實現Borrow 和BorrowMut
    的頭像 發表于 05-22 15:57 ?958次閱讀

    rust語言基礎學習: 智能指針之Cow

    Rust中與借用數據相關的三個trait: Borrow, BorrowMut和ToOwned。理解了這三個trait之后,再學習Rust中能夠實現
    的頭像 發表于 05-22 16:13 ?3012次閱讀

    rust語言基礎學習: Default trait

    Default trait 顧名思義是默認值,即Rust為特定類型實現 Default 特性時,可以為該類型賦予
    的頭像 發表于 05-22 16:16 ?1329次閱讀
    大发888xp缺少casino| 网上百家乐官网真坑人| 百家乐群121398015| 皇冠网代理| 足球百家乐官网网上投注| 带有百家乐的棋牌游戏有哪些| 平利县| 大家赢百家乐投注| 上饶市| 百家乐赌场代理荐| 网上百家乐官网如何作假| 个体老板做生意的风水| 乐百家娱乐| 真人百家乐是啥游戏| 蓝盾国际娱乐| 百家乐真人百家乐皇冠开户| 网上真钱梭哈| 百家乐楼梯缆| 网上百家乐官网有人赢过吗| 威尼斯人娱乐网假吗 | 百家乐庄家赢钱方法| 百家乐官网闲拉长龙| 劳力士百家乐的玩法技巧和规则| 百家乐官网赌场网| 大发888加速器| 百家乐胜率被控制| 克拉克百家乐官网下载| 银河百家乐的玩法技巧和规则| 百家乐官网天下第一庄| 大发888娱乐场下载 游戏平台| 娱乐城百家乐官网的玩法技巧和规则| 大发888娱乐游戏| 视频百家乐信誉| 新百家乐官网庄闲路单图记录| 星空棋牌大厅下载| 足球百家乐投注计算| gt百家乐官网平台| 北京德州扑克比赛| 百家乐平投注法| BB百家乐官网HD| 君豪棋牌是真的吗|