Rust 第16节 Trait
Trait 告诉编译器
某种类型具有那些并且可以与其他类型共享的功能
它的本质就是
不同类型具有的相同行为
声明一个trait
关键字 trait;只有方法签名,没有方法实现
pub trait Animal {// trait 的声明,一个trait中可以有多个方法fn say(&self) -> String;fn func1(&self){};fn func2(&self){};
}
实现该Trait 的类型,必须提供具体的方法实现;
即 类型要和Trait进行关联;
例如:
pub struct Dog {pub name : String,pub age : i32,
}impl Animal for Dog { //将该类型使用traitfn say(&self) -> String {format!("my name is {}",self.name)}
}
其他类型也可以跟这个Trait进行关联
pub struct Cat {pub name : String,pub age : i32,pub fun : String,
}impl Animal for Cat {fn say(&self) -> String {format!("my name is {},my age is {}",self.name,self.age)}}
调用
let d1 = Dog{name : "Tom".to_string(),age : 3,};let c1 = Cat {name : "Goj".to_string(),age : 2,fun : "xx".to_string(),};println!("{}",d1.say());println!("{}",c1.say());
注意:
这个类型 或者 这个 trait是本地定义的;
无法为外部类型 实现外部trait
Trait 的默认实现方法
pub trait Animal {// trait 的声明,一个trait中可以有多个方法fn say(&self) -> String;fn hello(&self) { //默认实现的方法println!("hello everyone!!");}fn goodbye(&self){println!("{},goodbye !!",self.say()); //默认实现的方法可以调用其他方法,即使这个方法不是默认实现的方法}
}
对于跟Trait关联的类型;可以对默认函数进行重新定义,即重构
impl Animal for Cat {fn say(&self) -> String {format!("my name is {},my age is {}",self.name,self.age)}fn hello(&self) { //在这个类型中,对默认方法进行了重构println!("hello everyone ,i'm {}",self.name);}
}
也可以不改变,使用默认方法
impl Animal for Dog { //将该类型使用traitfn say(&self) -> String {format!("my name is {}",self.name)}//在这个类型中,没有对trait的hello方法进行重构,所以使用的是默认的实现
}
Trait 作为参数
期望这个函数的参数是有Animal trait功能的类型
方法1 impl Trait 语法 使用于简单类型
pub fn speek(item : impl Animal) {println!("this is me, {}",item.say());
}
speek(c1);speek(d1);
方法2 Trait bound 语法
直译就是Trait 约束,适用于比较复杂的场景
pub fn speek_v2<T: Animal>(item : T) {println!("V2: {}",item.say());
}
//复杂场景
pub fn speek_v3<T:Animal>(item1 : T,item2 : T) {println!("v3: {},{}",item1.say(),item2.say());
}
speek_v2(c1);speek_v2(d1);speek_v3(d1, d2);
需要参数同时支持多个Trait
期望这个函数的参数是同时支持两个trait功能的类型
使用 + 号,来表示同时支持多个trait
pub fn two_speek(item : impl Animal + Funcitons) {println!("{}",item.say());item.my_function();
}
trait bound
pub fn two_speek_v2<T:Animal + Funcitons>(item : T ) {println!("{}",item.say());item.my_function();
}
当有更复杂场景时,使用where约束
pub fn speek_v4<T : Animal,U : Animal + Funcitons>(item1 : T,item2 : U) {item1.goodbye();item2.my_function();
}
where 约束
pub fn speek_v5<T,U>(item1 : T,item2 : U)
whereT : Animal,U : Animal + Funcitons,
{item1.goodbye();item2.my_function();
}
Trait 作为返回值
注意点,这个函数只能返回某一种具体的类型;返回可能得不同的类型,编译器会报错
pub fn create_animal(s : &str) -> impl Animal{Dog {name : String::from("s"),age : 12,}
}
泛型 加 Trait
定义一个泛型结构体
struct Pair<T> {x : T,y : T,
}
针对所有泛型T,定义一个方法:
impl<T> Pair<T> {fn new(x :T ,y : T) -> Self {Self {x , y}} //这个泛型结构体有一个new 函数
}
针对满足要求的Trait类型,定义一个方法:
impl<T : Display + PartialOrd> Pair<T> {
// 这里对泛型结构体中的 cmp_display() 方法进行了约束,只有类型T 满足 Display 和 PartialOrd 两个Trait时,才能有这个方法。fn cmp_display(&self) {if self.x >= self.y {println!("largest member is {}",self.x);} else {println!("largest member is {}",self.y);}}
}
覆盖约束
满足某一约束条件然后去实现另一Trait 的行为叫覆盖约束
例如to_string()方法
3.to_string();
数字3 可以调用to_string()方法,
是由于 3 有 display方法,
所有有display方法的类型,都被实现了 to_string 方法