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

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

3天內不再提示

實現(xiàn)Rust Trait類型 那么該類型的引用也實現(xiàn)了trait嗎?

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

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

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

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

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

/// BasicSpeaker是一個空結構體,只是為了實現(xiàn)Speaker。
structBasicSpeaker;

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

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

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

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

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

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

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

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

這是可行的,因為BasicSpeaker實現(xiàn)了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沒有實現(xiàn)trait Speaker”。

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

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

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

直接的解決方案

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

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

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

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

用泛型Trait解決這個問題

我可以寫一個泛型的實現(xiàn):“如果一個struct T實現(xiàn)了Speaker,那么寫一個用于&T的Speaker實現(xiàn)?!?/p>

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

總結

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

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






審核編輯:劉清

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

    關注

    0

    文章

    57

    瀏覽量

    3001

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

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

收藏 人收藏

    評論

    相關推薦

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

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

    Rust語言中的反射機制

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

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

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

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

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

    子vi如何使其變?yōu)榉菄栏?b class='flag-5'>類型

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

    Windows -編程-數(shù)據(jù)類型

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

    引用數(shù)據(jù)類型的概念_引用數(shù)據(jù)類型有哪幾種

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

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

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

    Rust原子類型和內存排序

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

    Rust中GAT和高階類型

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

    trait中使用 `async fn`

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

    重點講解Send與Sync相關的并發(fā)知識

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

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

    Borrow和BorrowMut traitRust標準庫std:borrow 模塊中用于處理借用數(shù)據(jù)的trait,通過實現(xiàn)Borrow 和BorrowMut
    的頭像 發(fā)表于 05-22 15:57 ?838次閱讀

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

    Rust中與借用數(shù)據(jù)相關的三個trait: Borrow, BorrowMut和ToOwned。理解了這三個trait之后,再學習Rust中能夠實現(xiàn)
    的頭像 發(fā)表于 05-22 16:13 ?2842次閱讀

    rust語言基礎學習: Default trait

    Default trait 顧名思義是默認值,即Rust為特定類型實現(xiàn) Default 特性時,可以為該類型賦予
    的頭像 發(fā)表于 05-22 16:16 ?1220次閱讀