特征
在为 list 命令实现 set_title 和 set_content 方法时, 我们使用了 Into<String> 进行类型约束。
但 Into<T> 并非是类型, 它是一个特征, 是 Rust 用于定义行为约定的一种机制。可以为类型定义统一的能力规范。
我们可以将 Rust 中的特征看作是其他语言中的接口。
Rust 内置了许多特征。例如 Into<T> 表示可以将类型转换为 T。From<T> 表示可以从 T 构造出某个类型。
Copy 和 Clone 可以表示一个类型是否可以被复制等等。
在最开始我们编写程序时就遇到过一个涉及特征的错误。
String 类型没有实现 Copy 特征, 因此无法直接将参数赋值。
6 | let title = args[1];
| ^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
声明特征
特征的声明使用 trait 关键字。
trait PrintName {
fn PrintName(&self) -> String;
}
与枚举一样, 特征只要使用了 pub 修饰, 那么它的方法就全部可供外部访问。
实现特征
特征的实现依赖于类型。在原先的为类型实现方法的基础上增加实现的特征名称和 for 关键字。
trait PrintName {
fn PrintName(&self) -> String;
}
impl PrintName for TodoItem {
fn PrintName(&self) -> String {
"TodoItem".to_string()
}
}
倘若需要为类型 A 实现特征 B, 那么两者必须有一个是在当前作用域中定义的。否则将无效。
例如想为 String 实现 Copy 特征, 但两者都定义在标准库中而不在当前作用域, 因此无法实现。
这个规则被称为孤儿规则, 它确保了他人编写的代码不会破坏我们的代码, 我们也不会莫名其妙破坏他人的代码。
特征约束
前面我们使用到了特征约束, 即 T: Into<String>。它表示 T 必须实现 Into<String> 特征。
特征约束不仅可以约束泛型, 也可以约束特征自身。
trait PrintName: Display {
fn PrintName(&self) -> String;
}
以上代码为 PrintName 特征的定义, 它要求实现对象必须实现 Display 特征才能实现它。
可以使用 + 号增加更多的约束。例如 trait PrintName: Display + Clone 表示 PrintName 必须实现 Display 和 Clone 特征才能实现它。
参数约束
特征也可以用来约束参数类型。
trait PrintName: Display {
fn PrintName(&self) -> String;
}
fn printName(item: &impl PrintName) {
println!("{}", item.PrintName());
}
// 以上等价于
fn printName<T: PrintName>(item: &T) {
println!("{}", item.PrintName());
}