概念
_trait_
告诉Rust
编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait
以一种抽象的方式定义共享的行为。可以使用_trait bounds_
指定泛型是任何拥有特定行为的类型。
Traits are kind of similar to interfaces in OOP languages. They are used to define the functionality a type must provide. Multiple traits can be implemented for a single type.
trait
相当于OOP
语言中的接口,被用来定义类型必须要具备的功能(方法),可以在单个类型上实现多个**trait**
But traits can also include default implementations of methods. Default methods can be overridden when implementing types.
trait
可以有方法的默认实现,为类型实现trait
时能重写方法
Other than functions, traits can contain constants and types.
trait
中还可以包含常量和类型(type
关键字)
And also in Rust, new traits can be implemented for existing types even for types like i8, f64 and etc. Same way existing traits can be implemented for new types you are creating. But we can not implement existing traits into existing types.
- 可以为原有类型实现自定义
**trait**
- 可以为自定义类型实现内置
**trait**
- 不能为原有类型实现内置
**trait**
,即不能重写为原有类型实现的内置trait
这个限制是被称为 相干性(coherence) 的程序属性的一部分,或者更具体的说是 孤儿规则(orphan rule),其得名于不存在父类型。这条规则确保了其他人编写的代码不会破坏你代码,反之亦然。没有这条规则的话,两个 crate
可以分别对相同类型实现相同的 trait
,而 Rust
将无从得知应该使用哪一个实现。
为类型实现(impl)trait
使用impl
关键字为类型实现trait
示例:
struct Player {
first_name: String,
last_name: String,
}
trait FullName {
fn full_name(&self) -> String;
}
impl FullName for Player {
fn full_name(&self) -> String {
format!("{} {}", self.first_name, self.last_name)
}
}
fn main() {
let player_2 = Player {
first_name: "Roger".to_string(),
last_name: "Federer".to_string(),
};
println!("Player 02: {}", player_2.full_name());
}
trait的继承(inheritance)
- 使用
:
指定父trait
- 使用
+
指定多个父trait
示例:
trait Person {
fn full_name(&self) -> String;
}
trait Employee : Person { // 指定父trait
fn job_title(&self) -> String;
}
trait ExpatEmployee : Employee + Expat { // 指定多个父trait
fn additional_tax(&self) -> f64;
}
trait bound
多态概念(polymorphic)
In computing, static dispatch is a form of polymorphism fully resolved during compile time. It is a form of method dispatch, which describes how a language or environment will select which implementation of a method or function to use.
在计算中,静态分派是一种在编译时完全解决的多态形式。它是方法分派的一种形式,它描述了语言或环境将如何选择要使用的方法或函数的实现。
While Rust favors static dispatch, it also supports dynamic dispatch through a mechanism called ‘trait objects.’
Rust
喜欢静态分派,但是它也支持通过“**trait**
对象(猜测可能是运行时的一个对象,通过链式查找到具体实例的方法实现)”机制实现动态分派
Dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time.
动态分派是指在运行时选择调用哪一个多态操作(方法或者函数)的实现的过程
trait bound
示例:
trait GetSound {
fn get_sound(&self) -> String;
}
struct Cat {
sound: String,
}
impl GetSound for Cat {
fn get_sound(&self) -> String {
self.sound.clone()
}
}
struct Bell {
sound: String,
}
impl GetSound for Bell {
fn get_sound(&self) -> String {
self.sound.clone()
}
}
// 指定为GetSound类型
fn make_sound<T: GetSound>(t: &T) {
println!("{}!", t.get_sound())
}
// 第二种写法,是第一种写法的语法糖
fn make_sound(t: impl GetSound) {
println!("{}!", t.get_sound())
}
fn main() {
let kitty = Cat { sound: "Meow".to_string() };
let the_bell = Bell { sound: "Ding Dong".to_string() };
// 多态
make_sound(&kitty); // Meow!
make_sound(&the_bell); // Ding Dong!
}
多个trait bound
可以通过 +
指定多个trait bound
pub fn notify(item: **impl** **Summary + Display**) { }
pub fn notify<**T**: **Summary + Display**>(item: **T**) {
通过 where 简化 trait bound
对于有多个泛型的函数,可以使用where从句指定trait bound
的语法
示例:
原写法:fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { }
新写法:fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{ }
trait bound
作为返回值
可以将**impl Summary**
放在返回值的位置,作为对返回值的类型,不过并不常用,因为方法体只能返回一种具体类型,否则会报错。
这在闭包和迭代器场景十分的有用。闭包和迭代器创建只有编译器知道的类型,或者是非常非常长的类型。impl Trait
允许你简单的指定函数返回一个 Iterator
而无需写出实际的冗长的类型。
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false,
}
}
为泛型T添加trait bound条件
在使用impl
为struct
实现方法时,可以为struct
的泛型T
添加trait bound
,对于一个struct
实例,其泛型T
对应的具体类型需要满足trait bound
条件。
因为创建struct
实例时,需要指定泛型T
具体是哪个类型,所以针对**T**
的不同,**T**
所实现的**trait**
不同,实例所能调用的方法也有限制。
示例:
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
// 对任何 T 类型有效
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self {
x,
y,
}
}
}
// 对泛型加trait bound条件
// 只对实现了 Display 和 PartialOrd 两个 trait 的 T 类型有效
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);
}
}
}