關(guān)于rust中trait(一)

概述

相對其他語言(java/C#)提供了接口來滿足對不同類型的值進行操作的代碼(甚至包括那些尚未實現(xiàn)的類型)柑船,并能夠結(jié)合泛型來實現(xiàn)語言的“多態(tài)化”霞溪;同樣Rust為了達到對“多態(tài)”的支持提供了trait(特型)和generic(泛型)。
而trait(特型)算是rust對接口或抽象基類的實現(xiàn)链嘀。
舉個類型: 標準庫中關(guān)于Write定義 std::io::Write

trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;
       fn write_all(&mut self, buf: &[u8]) -> Result<()> {
        // 省略部分代碼
       }
       // 省略代碼
}

同樣在標準類型庫中File兜叨、TcpStream和Vec<u8>等實現(xiàn)了std::io::Write

use std::io::Write; // 需要引入對應(yīng)的trait

fn say_hello(out: &mut Write) -> std::io::Result<()> {
    out,write_all(b"welcome to rust world!!!")?;    // 使用Vec<u8> 
    out.flush();
}

///  測試用例
use std::fs::File;
fn main() {
    let mut file = std::fs::File::create("hello_world.txt").unwrap();
    say_hello(&mut file);

    let mut bytes = vec![];
    say_hello(&mut bytes);
    println!("bytes={:?}", bytes);
}

同樣使用trait(特型)添加給類型不用額外內(nèi)存

使用trait(特型)

在rust中trait(特型)是一種任何類型都可以支持或不支持;trait可以認為某類型能夠做什么的一種能力谱姓;(當使用trait特型方法時需要確保其本身必須存在當前作用域中借尿,否則trait特型所有的方法都是不可用的;若是trait特型是標準前置模塊的一部分屉来,這樣rust就會默認自動將其引入)路翻。
1、trait object(特型對象/特型目標)

use std::io::Write;
fn main()  {
    let mut buf: Vec<u8> = vec![];

    // let writer: Write = buf; // 不能通過編譯茄靠,編譯時不知道write真實大小
    let writer: &mut Write = &mut buf; 
}

上面的例子中l(wèi)ine-error-1這行是不能通過編譯的帚桩,在rust中變量大小在編譯期間必須可知的,但是實現(xiàn)Write的類型大小是任意的嘹黔,這一點不同于其他語言(java/C#)账嚎,因為這些語言默認定義的變量是一個引用,并執(zhí)行任何實現(xiàn)了interface或abstract的對象儡蔓;但是在rust的引用必須是顯式的郭蕉。并且在rust將一個trait特型類型的引用稱為trait object(特型對象/特型目標)。
不過需要知道的是:trait object(特型對象/特型目標)在編譯期間通常是不知道引用對象/目標的類型喂江,那就需要trait object(特型對象/特型目標)包含一些關(guān)于trait object(特型對象/特型目標)的額外信息召锈,并且這個信息僅限r(nóng)ust內(nèi)部自身使用,確保在調(diào)用trait特型方法時知道類型信息获询,這樣才能動態(tài)調(diào)用正確的方法涨岁。

2、trait object(特型對象/特型目標)的內(nèi)存分布
在內(nèi)存中, trait object(特型對象/特型目標)是一個胖指針,包含指向值的指針和指向表示該值類型的表的指針吉嚣; 每個trait object(特型對象/特型目標)都會占用兩個機器字梢薪;

let mut buf: Vec<u8> = vec![];

let writer: &mut Write = &mut buf;  

內(nèi)存布局如下:

data:數(shù)據(jù)指針內(nèi)容 &mut buf as &mut Write 值類型的表指針
buf data:數(shù)據(jù)指針 destructor
buffer:數(shù)據(jù) vptr :值類型的表指針 size
capacity: 容量 alignment
length: 大小 .wirte()
.flush()
.write_all()

在rust中會在編譯時生成虛擬表并且只生成一次,并有同類型的所有對象共享(impl trait_name for traint_imple_name)尝哆;這樣在調(diào)用一個trait object(特型對象/特型目標)的方法時秉撇,會自動使用虛擬表,已確定調(diào)用了哪個實現(xiàn)秋泄。

3琐馆、traint特型的定義和實現(xiàn)
在前面的例子中定義trait特型其實是很簡單的:

關(guān)鍵字trait Trait_Name {

// trait體 定義不同的方法
// 方法

}

樣例:

trait Action {
    fn draw(&self, canvas: &mut Canvas);
    fn hit_test(&self, x:i32, y:i32) -> bool;
}

同樣,實現(xiàn)trait也是相對比較簡單的:

關(guān)鍵字 impl Trait_Name for Type_Name {
// 實現(xiàn)的方法

}

樣例:

impl Action for Broom {
    fn draw(&self, canvas: &mut Canvas) {
        for y in self.y - self.height -1 .. self.y {
            canvas.write_at(self.x, y, '|');
        }
        canvas.write_at(self.x, self.y, 'M');
    }

    fn hit_test(&self, x: i32, y: i32)  -> bool {
        self.x == x 
        && self.y - self.height - 1 <= y 
        && y <= self.y
    }
}

而非trait特型的方法恒序,可以使用impl Type_Name來定義:

impl Type_Name {
    fn method_name(&self, ...<參數(shù)>...);
    fn method_name(...<參數(shù)>...);
    ...
}

不過在實現(xiàn)trait時瘦麸,若是其中某個或某些方法相對通用,可以在trait特型中提供當前方法的默認實現(xiàn)歧胁。 這樣通過impl Trait_Name for Type_Name 語法來實現(xiàn)trait時滋饲,可以使用這個默認方法彤敛。

在Rust允許在任意類型上實現(xiàn)任意trait特型,這樣就可以給任意類型擴張功能:

樣例: 擴展現(xiàn)有char類型的功能

trait IsEmoji {
    fn is_emlji(&self) -> bool;
}

impl IsEmoji for char {
    fn is_emoji(&self) -> bool {
        ...
    }
}

同樣了赌,可以一次性全部實現(xiàn)一個擴展trait特型: 使用泛型impl語法讓一個類型“家族”都具有某個擴展trait特型(關(guān)于泛型這塊會有單獨的介紹)墨榄。

樣例:對每個實現(xiàn)Write的類型,在實現(xiàn)trait特型WriteHtml

use std::io::{self, Write};

trait WriteHtml {
    fn write_html(&mut self, html: &HtmlDocument) -> io::Result;
}

impl<W: Write> WriteHtml for W {
    fn write_html(&mut self, html: &HtmlDocument) -> io::Result<()> {
        ......
    }
}

在rust中trait特型也能夠?qū)崿F(xiàn)其他語言的“繼承”勿她, 通過impl來實現(xiàn)trait時需要實現(xiàn)當前trait特型及其父trait特型袄秩。

樣例:子trait特型

trait Trait_Name: Parent_Trait_Name {
    ...
}

引用

trait特型

rust之trait-知乎

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逢并,隨后出現(xiàn)的幾起案子之剧,更是在濱河造成了極大的恐慌,老刑警劉巖砍聊,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背稼,死亡現(xiàn)場離奇詭異,居然都是意外死亡玻蝌,警方通過查閱死者的電腦和手機蟹肘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俯树,“玉大人帘腹,你說我怎么就攤上這事⌒矶觯” “怎么了阳欲?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陋率。 經(jīng)常有香客問我球化,道長,這世上最難降的妖魔是什么瓦糟? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任筒愚,我火速辦了婚禮,結(jié)果婚禮上狸页,老公的妹妹穿的比我還像新娘锨能。我一直安慰自己,他們只是感情好芍耘,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著熄阻,像睡著了一般斋竞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秃殉,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天坝初,我揣著相機與錄音浸剩,去河邊找鬼。 笑死鳄袍,一個胖子當著我的面吹牛绢要,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拗小,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼重罪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哀九?” 一聲冷哼從身側(cè)響起剿配,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阅束,沒想到半個月后呼胚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡息裸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年蝇更,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呼盆。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡簿寂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宿亡,到底是詐尸還是另有隱情常遂,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布挽荠,位于F島的核電站克胳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏圈匆。R本人自食惡果不足惜漠另,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望跃赚。 院中可真熱鬧笆搓,春花似錦、人聲如沸纬傲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叹括。三九已至算墨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汁雷,已是汗流浹背净嘀。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工报咳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挖藏。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓暑刃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親膜眠。 傳聞我的和親對象是個殘疾皇子岩臣,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 之前的幾個帖子討論了Rust設(shè)計的兩大支柱特性: 無垃圾回收的安全內(nèi)存管理 無數(shù)據(jù)競爭(Data Race)風(fēng)險的...
    Nuk閱讀 8,533評論 0 18
  • 選擇 rust 的理由 Rust 有助于您提供代碼質(zhì)量讓我們更加明確地了解性能成本便于開發(fā)人員權(quán)衡代碼性能利弊 R...
    zidea閱讀 1,972評論 0 12
  • trait特性 trait特性可以理解為Java中的接口,具備和接口很類似的特性柴底。trait中的函數(shù)叫做方法婿脸。某個...
    生若夏花_1ad0閱讀 2,465評論 0 2
  • 簡介 Trait 類似其他語言中的接口,但是又不完全一樣柄驻,Rust 可以擴展的事兒更多狐树。 什么叫特征Trait 可...
    kami1983閱讀 2,004評論 0 0
  • 傳送門:深入淺出Rust(第一部分-1)[http://www.reibang.com/p/9c47ed661c...
    沉寂之舟閱讀 23,338評論 0 0