让我们再次审视我们的 Ticket 类型:

#![allow(unused)]
fn main() {
pub struct Ticket {
    title: String,
    description: String,
    status: String,
}
}

迄今为止,我们的所有测试都是使用 Ticket 的字段进行断言的。

#![allow(unused)]
fn main() {
assert_eq!(ticket.title(), "一个新的标题");
}

如果我们想直接比较两个 Ticket 实例会怎样呢?

#![allow(unused)]
fn main() {
let ticket1 = Ticket::new(/* ... */);
let ticket2 = Ticket::new(/* ... */);
ticket1 == ticket2
}

编译器会阻止我们:

error[E0369]: binary operation `==` cannot be applied to type `Ticket`
  --> src/main.rs:18:13
   |
18 |     ticket1 == ticket2
   |     ------- ^^ ------- Ticket
   |     |
   |     Ticket
   |
note: an implementation of `PartialEq` might be missing for `Ticket`

Ticket 是一个新类型。开箱即用,它没有任何行为附着于其上**。Rust 不会神奇地推断如何比较两个 Ticket 实例,仅仅因为它们包含了 Strings。

不过,Rust 编译器正把我们推向正确的方向:它提示我们可能缺少了 PartialEq 的实现。PartialEq 是一个特性

特性是什么?

特性是Rust定义接口的方式。
特性定义了一组类型必须实现的方法,以满足特性的契约。

定义特性

特性定义的语法如下:

#![allow(unused)]
fn main() {
trait <TraitName> {
    fn <method_name>(<parameters>) -> <return_type>;
}
}

例如,我们可能会定义一个名为 MaybeZero 的特性,要求其实现者定义一个 is_zero 方法:

#![allow(unused)]
fn main() {
trait MaybeZero {
    fn is_zero(self) -> bool;
}
}

实现特性

为类型实现特性时,我们使用 impl 关键字,就像我们为常规1方法那样,但语法略有不同:

#![allow(unused)]
fn main() {
impl <TraitName> for <TypeName> {
    fn <method_name>(<parameters>) -> <return_type> {
        // Method body
    }
}
}

例如,为自定义数字类型 WrappingU32 实现 MaybeZero 特性:

#![allow(unused)]
fn main() {
pub struct WrappingU32 {
    inner: u32,
}

impl MaybeZero for WrappingU32 {
    fn is_zero(self) -> bool {
        self.inner == 0
    }
}
}

调用特性方法

调用特性方法时,我们使用.操作符,就像调用常规方法一样:

#![allow(unused)]
fn main() {
let x = WrappingU32 { inner: 5 };
assert!(!x.is_zero());
}

要调用特性方法,两件事必须为真:

  • 类型必须实现了该特性。
  • 特性必须在作用域内。

为满足后者,你可能需要添加一个 use 语句来引入特性:

#![allow(unused)]
fn main() {
use crate::MaybeZero;
}

如果:

  • 特性在调用发生的同一模块中定义。
  • 特性定义在标准库的预置中。预置是一组自动导入到每个Rust程序中的特性和类型。这就像是在每个Rust模块开头添加了 use std::prelude::*;

你可以在Rust文档中找到预置中特性和类型的列表:https://doc.rust-lang.org/std/prelude/index.html

参考资料

  • 本节练习位于 exercises/04_traits/01_trait
1

直接定义在一个类型上,而不使用特性的方法,也称为固有方法