28.特性trait.rs
生活随笔
收集整理的這篇文章主要介紹了
28.特性trait.rs
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
/*
特性(trait)概念接近于 Java 中的接口(Interface),但兩者不完全相同。特性與接口相同的地方在于它們都是一種行為規范,可以用于標識哪些類有哪些方法
trait Descriptive {fn describe(&self) -> String;
}
Descriptive 規定了實現者必須有是 describe(&self) -> String 方法。(必須實現所有方法)幾個非常有用的標準庫特性:Drop提供了當一個值退出作用域后執行代碼的功能,它只有一個drop(&mut self)方法。Borrow用于創建一個數據結構時把擁有和借用的值看作等同。AsRef用于在泛型中把一個值轉換為引用。Deref<Target=T>用于把&U類型的值自動轉換為&T類型。Iterator用于在集合 (collection) 和惰性值生成器 (lazy value generator) 上實現迭代器。Sized用于標記運行時長度固定的類型,而不定長的切片和特性必須放在指針后面使其運行時長度已知, 比如&[T]和Box<Trait>。*///特性簡單使用
fn test_1() {trait Descriptive {fn describe(&self) -> String;}struct Person {name: String,age: u8,}impl Descriptive for Person {fn describe(&self) -> String {format!("{} {}", self.name, self.age)}}
}//特性不僅可以綁定結構體方法,還可以綁定結構體關聯方法
fn test_1_2() {trait Descriptive {fn describe() -> String;}struct Person {name: String,age: u8,}impl Descriptive for Person {fn describe() -> String {//format!("{} {}", self.name, self.age)String::from("1231111111111111111111")}}fn test2_1(t:impl Descriptive){println!("{}",Person::describe());}let p =Person{name: String::from("123"),age: 12,};test2_1(p);
}//默認特性
fn test_2() {//這是特性與接口的不同點:接口只能規范方法而不能定義方法,但特性可以定義方法作為默認方法,因為是"默認",//所以對象既可以重新定義方法,也可以不重新定義方法使用默認的方法trait Descriptive {fn describe(&self) -> String {String::from("[Object]")}}struct Person {name: String,age: u8,}impl Descriptive for Person {//如果想調用特性的默認接口,把下面這段代碼去掉就可以了fn describe(&self) -> String {format!("{} {}", self.name, self.age)}}let cali = Person {name: String::from("Cali"),age: 24,};println!("{}", cali.describe());
}//特性做參數
fn test_3() {trait Descriptive {fn describe(&self) -> String {String::from("[Object]")}}//任何實現了 Descriptive 特性的對象都可以作為這個函數的參數,這個函數沒必要了解傳入對象有沒有其他屬性或方法,//只需要了解它一定有 Descriptive 特性規范的方法就可以了。當然,此函數內也無法使用其他的屬性與方法。fn output(object: impl Descriptive) {println!("{}", object.describe());}//特性參數還可以用這種等效語法實現:fn output2<T: Descriptive>(object: T) {println!("{}", object.describe());}//這是一種風格類似泛型的語法糖,這種語法糖在有多個參數類型均是特性的情況下十分實用:fn output3<T: Descriptive>(arg1: T, arg2: T) {println!("{}", arg1.describe());println!("{}", arg2.describe());}//特性作類型表示時如果涉及多個特性,可以用 + 符號表示,例如:trait Summary {fn describe(&self) -> String {String::from("[Summary]")}}trait Display {fn describe(&self) -> String {String::from("[Display]")}}fn output4(item: impl Summary + Display) {}fn output5<T: Summary + Display>(item: T) {}struct Person {name: String,age: u8,}impl Descriptive for Person {//如果想調用特性的默認接口,把下面這段代碼去掉就可以了fn describe(&self) -> String {format!("{} -- {}", self.name, self.age)}}impl Summary for Person {fn describe(&self) -> String {format!("{} -Summary- {}", self.name, self.age)}}impl Display for Person {fn describe(&self) -> String {format!("{} -Display- {}", self.name, self.age)}}/*/------------------//復雜的實現關系可以使用 where 關鍵字簡化,例如:fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)->i32{}//可以簡化成:fn some_function<T, U>(t: T, u: U) -> i32where T: Display + Clone,U: Clone + Debug//------------------*/impl Descriptive for () {//如果想調用特性的默認接口,把下面這段代碼去掉就可以了fn describe(&self) -> String {format!("6666666666666666666666")}}let p = Person {name: String::from("liujiayu"),age: 30,};output(p);//測試()實現特性output2(());let p = Person {name: String::from("liujiayu"),age: 30,};output4(p);
}//特性做參數實現取最大數
fn test_4() {// 由于需要聲明 compare 函數的第二參數必須與實現該特性的類型相同,所以 Self (注意大小寫)關鍵字就代表了當前類型(不是實例)本身。trait Comparable {fn compare(&self, object: &Self) -> i8;}fn max<T: Comparable>(array: &[T]) -> &T {let mut max_index = 0;let mut i = 1;while i < array.len() {if array[i].compare(&array[max_index]) > 0 {max_index = i;}i += 1;}&array[max_index]}impl Comparable for f64 {fn compare(&self, object: &f64) -> i8 {if &self > &object {1} else if &self == &object {0} else {-1}}}let arr = [1.0, 3.0, 5.0, 4.0, 2.0];println!("maximum of arr is {}", max(&arr));
}//特性做返回值
fn test_5() {trait Descriptive {fn describe(&self) -> String;}struct Person {name: String,age: u8,}impl Descriptive for Person {fn describe(&self) -> String {format!("{} {}", self.name, self.age)}}fn person() -> impl Descriptive {Person {name: String::from("Cali"),age: 24,}}//但是有一點,特性做返回值只接受實現了該特性的對象做返回值且在同一個函數中所有可能的返回值類//型必須完全一樣。比如結構體 A 與結構體 B 都實現了特性 Trait,下面這個函數就是錯誤的:struct A {}struct B {}impl Descriptive for A {fn describe(&self) -> String {String::from("A")}}impl Descriptive for B {fn describe(&self) -> String {String::from("B")}}// 編譯不過// fn some_function(bl:bool) -> impl Descriptive {// if bl {// return A {};// } else {// return B {};// }// }//some_function(false);
}//有條件實現方法
fn test_6() {//impl 功能十分強大,我們可以用它實現類的方法。但對于泛型類來說,有時我們需要區分一下它所屬的泛型已經實現的方法來決定它接下來該實現的方法//這段代碼聲明了 A<T> 類型必須在 T 已經實現 B 和 C 特性的前提下才能有效實現此 impl 塊。//(編譯不過,有機會再研究)// struct A<T> {}// impl<T: B + C> A<T> {// fn d(&self) {}// }
}//trait中可以繼承.繼承的意義是什么,這個例子沒有體現??????
fn test_7() {println!("---------------test_7--------------");trait PrintInfo {/// 方法/函數,沒有具體實現fn info(&self) {println!("base function---default");}}// InheritTrait這個trait,繼承PrintInfotrait InheritTrait: PrintInfo {fn out_info(&self) {println!("inherit function---default");}}#[derive(Debug)]struct People {id: u32, //idname: &'static str, //姓名 字符串sex: &'static str, //性別}impl PrintInfo for People {fn info(&self) {println!("base function---Nodefault")}}impl InheritTrait for People {fn out_info(&self) {println!("inherit function---Nodefault");}}let p = People {id: 1,name: "tom",sex: "man",};p.info();p.out_info();
}//關聯類型
fn test_8() {println!("---------test_8-------");/*定義:關聯類型(associated types)是一個將類型占位符與 trait 相關聯的方式,這樣trait的方法簽名中就可以使用這些占位符類型。trait的實現者會針對特定的實現在這個類型的位置指定相應的具體類型。如此可以定義一個使用多種類型的trait,直到實現此trait時都無需知道這些類型具體是什么。我們閱讀Rust程序的時候,有時候會出現如下的代碼:trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;}解釋:Iterator trait 有一個關聯類型Item。Item是一個占位類型,同時next方法會返回Option<Self::Item>類型的值。重點:"這個trait的實現者會指定Item的具體類型。"自己的理解:這就是一個迭代器,具體遍歷的是啥暫時不寫死,有可能是整數類型,也有可能是字符串類型*/pub trait Tt {}impl Tt for i32 { }impl Tt for String { }/*type Item : Tt;Tt是Item的約束,必須是trait類型,代表的意思是指定Item類型A的時候,A必須實現了Tt才行*/pub trait Watch {type Item : Tt;fn inner(&self) -> Option<Self::Item>;}struct A {data: i32,}impl Watch for A {type Item = i32;fn inner(&self) -> Option<Self::Item> {Some(self.data)}}struct B {data: String,}impl Watch for B {type Item = String;fn inner(&self) -> Option<Self::Item> {Some(self.data.clone())}}let a = A{data: 10};let b = B{data: String::from("B")};assert_eq!(Some(10), a.inner());assert_eq!(Some(String::from("B")), b.inner());
}//trait繼承的意義:子trait可以使用父trait的關聯類型
fn test_9() {println!("---------test_9-------");trait tt1 {type AccountID;}struct Empty{}impl tt1 for Empty {type AccountID = u64;}// InheritTrait這個trait,繼承PrintInfotrait tt11:tt1 {fn out_info(&self);}impl tt11 for Empty {fn out_info(&self) {println!("inherit function---default");let id:Self::AccountID = 1001;//注意帶上Self:: 進行訪問}}
}//trait繼承的意義:子trait可以使用父trait的關聯函數
fn test_9_2() {println!("---------test_9_2-------");trait TraitA {fn test_fn_traita();fn test_fn_traita_self(&self);}trait TraitA2 {fn test_fn_traita2();}trait TraitB: TraitA {fn test_fn_traitb();fn test_fn_traitb_self(&self);}trait TraitC: TraitB {fn test_fn_traitc();fn test_fn_traitc_self(&self);}struct StEmpty {}impl TraitA for StEmpty {fn test_fn_traita() {println!("fn :test_fn_traita, trait : TraitA, struct :StEmpty");}fn test_fn_traita_self(&self) {println!("fn :test_fn_traita_self, trait : TraitA, struct :StEmpty");}}impl TraitB for StEmpty {fn test_fn_traitb() {println!("fn :test_fn_traitb, trait : TraitB, struct :StEmpty");Self::test_fn_traita();}fn test_fn_traitb_self(&self) {println!("fn :test_fn_traitb_self, trait : TraitB, struct :StEmpty");//也可以調用帶self的self.test_fn_traita_self();}}let t = StEmpty{};t.test_fn_traitb_self();impl TraitC for StEmpty {fn test_fn_traitc() {println!("fn :test_fn_traitc, trait : TraitC, struct :StEmpty");Self::test_fn_traita(); //實驗證明可以調用他爺爺的接口}fn test_fn_traitc_self(&self) {}}// 多重繼承trait TraitD: TraitA2 + TraitC {fn test_fn_traitd();}impl TraitA2 for StEmpty {fn test_fn_traita2() {println!("fn :test_fn_traita2, trait : TraitA2, struct :StEmpty");}}impl TraitD for StEmpty {fn test_fn_traitd() {println!("fn :test_fn_traitd, trait : TraitD, struct :StEmpty");Self::test_fn_traita2();Self::test_fn_traitc(); // TraitCSelf::test_fn_traitb(); // TraitC 的爸爸Self::test_fn_traita(); // TraitC 的爺爺 - 都可以調用}}StEmpty::test_fn_traitd();
}//一個結構體有兩個特性,他們的函數集有交集
fn test_10(){println!("---------test_10-------");trait TraitOne {fn action(&self) {println!("action of trait one!")}}trait TraitTwo {fn action(&self) {println!("action of trait two!");}}struct Person {}impl TraitOne for Person {}impl TraitTwo for Person {}let p = Person {};//p.action(); 編譯失敗,不支持這種調用方式<Person as TraitOne>::action(&p);<Person as TraitTwo>::action(&p);
}fn test_11() {/*trait特性之間的關聯最初學習From和Into特性的時候,教程說只要實現了From特性就會自動實現Into特性,反之亦然。感覺很是奇怪,竟然還可以這樣,今天我們看一個類似的例子:只要實現了GetName特性也就自動實現了PrintName特性,不知道From和Into是不是這么做的,這個以后有機會再研究把吧,*/trait GetName {fn get_name(&self) -> &String;}trait PrintName {fn print_name(&self);}//只要是實現了GetName特性的結構體,都可以實現PrintNameimpl<T: GetName> PrintName for T {fn print_name(&self) {println!("name = {}", self.get_name());}}///-----------------------下面是測試代碼------------------struct Student {name: String,}impl GetName for Student {fn get_name(&self) -> &String {&(self.name)}}let s = Student {name: String::from("xiao ming"),};s.print_name();
}fn main() {test_1();test_2();test_3();test_4();test_5();test_6();test_7();test_8();test_9();test_9_2();test_10();test_11();
}
?
總結
以上是生活随笔為你收集整理的28.特性trait.rs的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 27.泛型generics.rs
- 下一篇: 29.rust类型转换.rs