rust中trait的使用方法詳解
介紹
trait用中文來講就是特征,它就是一個(gè)標(biāo)記,只不過這個(gè)標(biāo)記被用在特定的地方,也就是類型參數(shù)的后面,用來限定這個(gè)類型參數(shù)可能的類型范圍。trait是一種約束。
具體關(guān)系為: 變量(值空間太過寬泛,添加約束) -> 類型(約束過死,放開約束) -> 泛型(類型空間太過寬泛,添加約束) -> trait
語法上,T: TraitA意思就是對類型參數(shù)T施加TraitA這個(gè)標(biāo)記,具體實(shí)現(xiàn)為:
trait TraitA {} struct Atype; impl TraitA for Atype {}
對于某個(gè)類型T來說,如果它實(shí)現(xiàn)了這個(gè)TraitA,這個(gè)類型就滿足約束
fn print<T: std::fmt::Display>(p: Point<T>){...}
上面這這段代碼的意思是,Display對類型參數(shù)T做了約束,要求將來要帶入的具體類型必須實(shí)現(xiàn)Display這個(gè)trait。也就是說,trait對類型參數(shù)施加約束的同時(shí),也對具體的類型提供了能力,在Rust中約束和能力就是一體兩面,是同一個(gè)東西。
trait中包含什么
trait里面可以包含關(guān)聯(lián)函數(shù)、關(guān)聯(lián)類型和關(guān)聯(lián)常量。
關(guān)聯(lián)函數(shù)
// 下列代碼涉及所有權(quán)三態(tài) trait Sport { fn play(&self) {} // 注意這里一對花括號,就是trait的關(guān)聯(lián)函數(shù)的默認(rèn)實(shí)現(xiàn) fn play_mut(&mut self); fn play_own(self); fn play_some() -> Self; } struct Football; impl Sport for Football { // 由于play函數(shù)在trait中有關(guān)聯(lián)函數(shù)的默認(rèn)實(shí)現(xiàn),結(jié)構(gòu)體則可以不實(shí)現(xiàn)此函數(shù) fn play_mut(&mut self) {} fn play_own(self) {} fn play_some() -> Self { Self } } fn main() { let mut f = Football; f.play(); f.play_mut(); f.play_own(); let _g = Football::play_some(); let _g = <Football as Sport>::play_some(); // 等同于上一條代碼 }
關(guān)聯(lián)類型
在trait中,可以帶一個(gè)或多個(gè)關(guān)聯(lián)類型。關(guān)聯(lián)類型起一個(gè)類型占位功能,定義trait時(shí)聲明,在把trait實(shí)現(xiàn)到結(jié)構(gòu)體上的時(shí)候?yàn)槠渲付ň唧w的類型。
pub trait Sport { type ST; // 聲明關(guān)聯(lián)類型 fn play(&self, st: Self::ST); // 將關(guān)聯(lián)類型應(yīng)用到關(guān)聯(lián)函數(shù) } struct Football; pub enum SportType { Land, Water, } impl Sport for Football { type ST = SportType; // 為關(guān)聯(lián)類型指定具體類型 fn play(&self, st: Self::ST){} // 方法中用到關(guān)聯(lián)類型 } fn main() { let f = Football; f.play(SportType::Land); }
在T上使用關(guān)聯(lián)類型
trait TraitA { type Mytype; } fn doit<T: TraitA>(a: T::Mytype) {} // 這里在函數(shù)中使用關(guān)聯(lián)類型 struct TypeA; impl TraitA for TypeA { type Mytype = String; // 具化關(guān)聯(lián)類型為String } fn main() { doit::<TypeA>("abc".to_string()); // 指定泛型T為結(jié)構(gòu)體TypeA }
在約束中具化關(guān)聯(lián)類型
trait TraitA { type Item; } // 意思就是限制x必須是實(shí)現(xiàn)TraitA而且它的關(guān)聯(lián)類型Item必須是String的類型 struct Foo<T: TraitA<Item=String>> { x: T }
對關(guān)聯(lián)類型的約束
在定義關(guān)聯(lián)類型的時(shí)候,也可以給關(guān)聯(lián)類型添加約束。后面在具化這個(gè)類型的時(shí)候,那些類型必須要滿足于這些約束
use std::fmt::Debug; trait TraitA { type Item: Debug; // 這里對關(guān)聯(lián)類型添加了Debug的約束 } #[derive(Debug)] struct A; // 這里在結(jié)構(gòu)體A上自動derive Debug約束 struct B; impl TraitA for B { type Item = A; // 這里類型A已經(jīng)滿足Debug約束 }
在使用時(shí)可以加強(qiáng)關(guān)聯(lián)類型的約束
... fn doit<T>(a: T) where T: TriatA, // 約束T類型必須實(shí)現(xiàn)TraitA T::Item: Debug + PartialEq, // 同時(shí)約束trait的關(guān)聯(lián)類型必須實(shí)現(xiàn)Debug和PartialEq { }
關(guān)聯(lián)常量
和關(guān)聯(lián)類型不同的是,關(guān)聯(lián)常量可以在trait定義的時(shí)候指定,也可以在給具體類型實(shí)現(xiàn)的時(shí)候指定。
trait TraitA { const LEN: u32 = 10; } struct A; impl TraitA from A { const LEN: u32 = 12; }
where
當(dāng)類型參數(shù)后面有對個(gè)trait約束的時(shí)候,會顯得頭重腳輕,所以Rust提供了where語法
fn doit<T: A + B + C + D + E + F>(t: T) -> i32 {} fn doit<T>(t: T) -> u32 where T: A + B + C + D + E + F {}
約束依賴
如果某種類型要實(shí)現(xiàn)TraitA,那么它也要同時(shí)實(shí)現(xiàn)TraitB。
trait TraitB {} trait TraitA: TraitB {} // 等價(jià)于 trait TraitC where Self: TraitB {}
約束之間是完全平等的,沒有上下級關(guān)系
約束中同名方法的訪問
trait Shape { fn play(&self) { println!("1"); } } trait Circle: Shape { fn play(&self) { println!("2"); } } struct A; impl Shape for A {} impl Circle for A {} impl A { fn play(&self) { println!("3"); } } fn main() { let a = A; a.play(); // 調(diào)用類型A上實(shí)現(xiàn)的play方法 <A as Circle>::play(&a); // 調(diào)用trait Circle上的play方法 <A as Shape>::play(&a); // 調(diào)用trait Shape上的play方法 }
這種語法叫做完全限定語法,是調(diào)用類型上某個(gè)方法的完整路徑表達(dá)。
用trait實(shí)現(xiàn)能力配置
trait提供了尋找方法的范圍
- 檢查有沒有直接在這個(gè)類型上實(shí)現(xiàn)這個(gè)方法
- 檢查有沒有在這個(gè)類型上實(shí)現(xiàn)某個(gè)trait,trait中有這個(gè)方法 一個(gè)類型可能實(shí)現(xiàn)了多個(gè)trait,不同的trait中各有一套方法,這些不同的方法中可能還會出現(xiàn)同名方法。Rust在這里采用了一種惰性的機(jī)制,由開發(fā)者指定在當(dāng)前的mod或scope中使用哪套或哪幾套能力。因此,對應(yīng)地需要開發(fā)者手動地將要用到的trait引入當(dāng)前scope。
mod module_a { pub trait Shape { fn play(&self) { println!("1"); } } pub struct A; impl Shape for A {} } mod module_b { use supper::module_a::Shape; // 需要同時(shí)引入A用到的trait use super::module_a::A; fn doit() { let a = A; a.play(); } }
孤兒原則
為了不導(dǎo)致混亂,Rust要求在一個(gè)模塊中,如果要對一個(gè)類型實(shí)現(xiàn)某個(gè)trait,這個(gè)類型和這個(gè)trait其中必須有一個(gè)是在當(dāng)前模塊定義的,如果必須用的話,可以用Newtyoe模式
Blanket Implementation
統(tǒng)一實(shí)現(xiàn)后,就不要對某個(gè)具體的類型再實(shí)現(xiàn)一次了,因?yàn)橥粋€(gè)trait只能實(shí)現(xiàn)一次到某個(gè)類型上。這個(gè)不像對類型做impl,可以實(shí)現(xiàn)多次(函數(shù)名要不沖突)。
trait TraitA {} trait TraitB {} impl<T: TraitB> TraitA for T {} // 為所有被TraitB約束的類型實(shí)現(xiàn)TraitA impl TraitB for u32 {} // impl TraitA for u32 {} // 無法再次實(shí)現(xiàn)
到此這篇關(guān)于rust中trait的使用方法詳解的文章就介紹到這了,更多相關(guān)rust trait內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust中的Cargo構(gòu)建、運(yùn)行、調(diào)試
Cargo是rustup安裝后自帶的,Cargo?是?Rust?的構(gòu)建系統(tǒng)和包管理器,這篇文章主要介紹了Rust之Cargo構(gòu)建、運(yùn)行、調(diào)試,需要的朋友可以參考下2022-09-09為什么要使用 Rust 語言、Rust 語言有什么優(yōu)勢
雖然 Rust 是一種通用的多范式語言,但它的目標(biāo)是 C 和 C++占主導(dǎo)地位的系統(tǒng)編程領(lǐng)域,很多朋友會問rust語言難學(xué)嗎?rust語言可以做什么,今天帶著這些疑問通過本文詳細(xì)介紹下,感興趣的朋友一起看看吧2022-10-10