trait¶
一、概述¶
Trait告诉Rust编译器,某种类型具有哪些并且可以与其他类型共享的功能,可以使用抽象的方式定义共享行为。Trait bounds
(约束)指的是泛型类型参数指定实现了特定行为的类型。
Trait与其他语言的借口(interface)类似,但是有区别。
二、定义Trait¶
定义Trait的目的是把方法签名放在一起,来实现某种目的所必须的一组行为。定义trait的相关规则如下
- 关键字:trait
- 只有方法签名,没有具体实现
- trait可以有多个方法,每个方法占一行,以
;
结尾 - 实现该trait的类型必须提供具体方法的实现
如下定义trait的示例代码
三、在类型上实现trait¶
与类型的实现方法类似,如下示例代码
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false
};
println!("1 new tweet: {}", tweet.summarize());
}
四、实现某个trait的约束¶
可以在某个类型上实现某个trait的前提条件为:这个类型或这个trait在本地crate里定义的。
无法为外部类型来实现外部的trait,这个限制是程序属性的一部分(也就是一致性),更具体地说是**孤儿原则**,之所以这样命名是因为父类型不存在。此规则确保他人的代码不能破坏你写的代码,反之亦然。如果没有这个规则,两个crate可以为同一个类型实现同一个trait,rust就不知道应该使用哪个实现了。
五、默认实现¶
trait可以拥有默认实现,当我们为某个类型实现trait时,可以选择重载或者保留原有的默认实现。默认实现的方法也可以调用trait中其他的方法,即使这些方法没有默认实现。如下示例代码
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
pub struct NewArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewArticle {
fn summarize_author(&self) -> String {
format!("@{} ", self.author)
}
}
需要注意的是,我们无法在方法的重写实现里调用默认的实现。
六、使用trait作为参数¶
6.1 impl Trait¶
使用impl Trait
的语法,可以适用于简单的函数传参情况。加入我们希望传入的参数实现了一个Summary
这个trait,那么我们可以使用如下的参数声明方式
那么我们在调用此函数的使用,可以传入Tweet
,也可以传入NewArticle
。
6.2 Trait bound¶
使用Trait bound
可以与复杂的情况,如下示例代码
如果宝航两个参数,使用Trait bound
的写法如下
实际上impl Trait
是Trait bound
的语法糖。
6.3 使用+指定多个trait bound¶
如果使用impl Trait
的方式,示例代码如下
use std::fmt::Display;
pub fn notify(item: impl Summary + Display) {
println("breaking news! {}", item.summarize());
}
使用Trait bound
的写法如下
use std::fmt::Display;
pub fn notify<T: Summary + Display>(item: T) {
println("breaking news! {}", item.summarize());
}
6.4 在方法签名后面使用where子句¶
如果一个函数要求的变量需要实现过多的Trait,代码的可读性会变低,很不直观,如下示例代码
pub fn notify<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
format!("breaking news! {}", a.summarize());
}
我们可以使用where子句还简化Trait的约束,如下示例代码
pub fn notify<T, U>(a: T, b: U) -> String
where T: Summary + Display,
U: Clone + Debug,
{
format!("breaking news! {}", a.summarize());
}
七、使用trait作为返回值类型¶
使用impl Trait
声明返回的类型为Trait,如下示例代码
使用imple Trait
只能返回确定的同一种类型,返回可能不同类型的代码会报错,如下代码将报错
pub fn notify(flag: bool) -> impl Summary {
if flag {
NewArticle {
// ......
}
} else {
Tweet {
// ......
}
}
}
八、使用PartialOrd实现数据比较¶
如下示例代码
use std::result;
fn largest<T: PartialOrd +Clone>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list.iter() {
if item > &largest {
largest = item;
}
}
largest
}
fn main() {
let str_list = vec![String::from("hello"), String::from("world")];
let result = largest(&str_list);
println!("The largest word id {}", result);
}
九、使用Trait Bound有条件的实现方法¶
在使用泛型类型参数的 impl 块上使用Trait bound,我们可以有条件的胃了实现特定 trait 的类型来实现。如下示例代码
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
// 所有Pair<T> 都拥有new函数
impl <T> Pair<T>{
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 实现了Display 和 PartialOrd 的Pair<T> 猜拥有com_display函数
impl <T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("the largest member is x = {}", self.x);
} else {
println!("the largest member is y = {}", self.y);
}
}
}
fn main(){
}
也可以为实现其他Trait的类型有条件的实现某个Trait。为满足Trait Bound的所有类型上实现Trait叫做覆盖实现(Blanket implementations)。