rust中trait的使用方法詳解
介紹
trait用中文來講就是特征,它就是一個標(biāo)記,只不過這個標(biāo)記被用在特定的地方,也就是類型參數(shù)的后面,用來限定這個類型參數(shù)可能的類型范圍。trait是一種約束。
具體關(guān)系為: 變量(值空間太過寬泛,添加約束) -> 類型(約束過死,放開約束) -> 泛型(類型空間太過寬泛,添加約束) -> trait
語法上,T: TraitA意思就是對類型參數(shù)T施加TraitA這個標(biāo)記,具體實現(xiàn)為:
trait TraitA {}
struct Atype;
impl TraitA for Atype {}
對于某個類型T來說,如果它實現(xiàn)了這個TraitA,這個類型就滿足約束
fn print<T: std::fmt::Display>(p: Point<T>){...}
上面這這段代碼的意思是,Display對類型參數(shù)T做了約束,要求將來要帶入的具體類型必須實現(xiàn)Display這個trait。也就是說,trait對類型參數(shù)施加約束的同時,也對具體的類型提供了能力,在Rust中約束和能力就是一體兩面,是同一個東西。
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)實現(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)實現(xiàn),結(jié)構(gòu)體則可以不實現(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中,可以帶一個或多個關(guān)聯(lián)類型。關(guān)聯(lián)類型起一個類型占位功能,定義trait時聲明,在把trait實現(xiàn)到結(jié)構(gòu)體上的時候為其指定具體的類型。
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必須是實現(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)類型的時候,也可以給關(guān)聯(lián)類型添加約束。后面在具化這個類型的時候,那些類型必須要滿足于這些約束
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約束
}
在使用時可以加強關(guān)聯(lián)類型的約束
...
fn doit<T>(a: T)
where
T: TriatA, // 約束T類型必須實現(xiàn)TraitA
T::Item: Debug + PartialEq, // 同時約束trait的關(guān)聯(lián)類型必須實現(xiàn)Debug和PartialEq
{
}
關(guān)聯(lián)常量
和關(guān)聯(lián)類型不同的是,關(guān)聯(lián)常量可以在trait定義的時候指定,也可以在給具體類型實現(xiàn)的時候指定。
trait TraitA {
const LEN: u32 = 10;
}
struct A;
impl TraitA from A {
const LEN: u32 = 12;
}
where
當(dāng)類型參數(shù)后面有對個trait約束的時候,會顯得頭重腳輕,所以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
{}
約束依賴
如果某種類型要實現(xiàn)TraitA,那么它也要同時實現(xiàn)TraitB。
trait TraitB {}
trait TraitA: TraitB {}
// 等價于
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上實現(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)用類型上某個方法的完整路徑表達。
用trait實現(xiàn)能力配置
trait提供了尋找方法的范圍
- 檢查有沒有直接在這個類型上實現(xiàn)這個方法
- 檢查有沒有在這個類型上實現(xiàn)某個trait,trait中有這個方法 一個類型可能實現(xiàn)了多個trait,不同的trait中各有一套方法,這些不同的方法中可能還會出現(xiàn)同名方法。Rust在這里采用了一種惰性的機制,由開發(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; // 需要同時引入A用到的trait
use super::module_a::A;
fn doit() {
let a = A;
a.play();
}
}
孤兒原則
為了不導(dǎo)致混亂,Rust要求在一個模塊中,如果要對一個類型實現(xiàn)某個trait,這個類型和這個trait其中必須有一個是在當(dāng)前模塊定義的,如果必須用的話,可以用Newtyoe模式
Blanket Implementation
統(tǒng)一實現(xiàn)后,就不要對某個具體的類型再實現(xiàn)一次了,因為同一個trait只能實現(xiàn)一次到某個類型上。這個不像對類型做impl,可以實現(xiàn)多次(函數(shù)名要不沖突)。
trait TraitA {}
trait TraitB {}
impl<T: TraitB> TraitA for T {} // 為所有被TraitB約束的類型實現(xiàn)TraitA
impl TraitB for u32 {}
// impl TraitA for u32 {} // 無法再次實現(xiàn)
到此這篇關(guān)于rust中trait的使用方法詳解的文章就介紹到這了,更多相關(guān)rust trait內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
rust標(biāo)準(zhǔn)庫std::env環(huán)境相關(guān)的常量
在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見的兩種方式和處理環(huán)境變量的兩種常見方式, 拋開Rust的語法, 實際上在命令行參數(shù)的處理方式上, 與其它語言大同小異, 可能影響我們習(xí)慣的也就只剩下語法,本文介紹rust標(biāo)準(zhǔn)庫std::env的相關(guān)知識,感興趣的朋友一起看看吧2024-03-03
教你使用RustDesk?搭建一個自己的遠程桌面中繼服務(wù)器
這篇文章主要介紹了RustDesk?搭建一個自己的遠程桌面中繼服務(wù)器,主要包括服務(wù)端安裝和客戶端配置方法,配置好相關(guān)操作輸入控制碼即可發(fā)起遠程或文件傳輸,本文通過圖文給大家講解的非常詳細,需要的朋友可以參考下2022-08-08

