Rust 高级 trait

泛型

例如定义两个类型,一个是 Millimeters,一个是 Meters,并且定义一个 add 方法,用于将两个类型相加。

1
2
struct Millimeters(u32);
struct Meters(u32);

看看 Add trait 的定义:

1
2
3
4
5
trait Add<Rhs=Self> {
type Output;

fn add(self, rhs: Rhs) -> Self::Output;
}

其中 Rhs 是一个泛型类型,Self 是实现 Add trait 的类型。type Outputadd 方法的返回类型。

为了实现两个不同类型的相加,需要将 Rhs 设置为 MetersSelf 设置为 MillimetersOutput 设置为 Millimeters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::ops::Add;

impl Add<Meters> for Millimeters {
type Output = Millimeters;

fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}

fn main() {
let a = Millimeters(10);
let b = Meters(1);
dbg!(a + b);
}

输出结果:

1
2
3
[src/main.rs:19] a + b = Millimeters(
1010,
)

完全限定语法

完全限定语法(fully qualified syntax)可以用来消除歧义,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
trait Pilot {
fn fly(&self);
}

trait Wizard {
fn fly(&self);
}

struct Human;

impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}

impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}

impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}

fn main() {
let person = Human;
Pilot::fly(&person);
Wizard::fly(&person);
person.fly();
}

输出:

1
2
3
This is your captain speaking.
Up!
*waving arms furiously*

扩展 trait

实现一个 trait 时,可以使用 trait TraitName: AnotherTraitName 语法来扩展 trait。

1
2
3
4
5
6
7
8
9
10
11
12
13
use std::fmt;

trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}

Point 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Point {
x: i32,
y: i32,
}

impl OutlinePrint for Point {}

use std::fmt;

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

fn main() {
let p = Point { x: 1, y: 3 };
p.outline_print();
}

输出:

1
2
3
4
5
**********
* *
* (1, 3) *
* *
**********

孤儿规则

孤儿规则(orphan rule)是 Rust 中的一个限制,它规定了只有当 trait 或类型对于当前 crate 是本地的时候,才可以在该类型上实现该 trait。这样可以避免不同 crate 之间出现相同的 trait 实现的冲突。要绕过这个限制,可以使用 newtype 模式,即在一个元组结构体中封装其类型,然后在这个新类型上实现 trait。

使用这个模式没有运行时性能惩罚,这个封装类型在编译时就被省略了。

例如,Vec<T> 实现 Display trait 是已被定义,但是我们可以定义一个新类型 Wrapper,并为它实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::fmt;

struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}

fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}

或者你可以使用 type alias

1
2
3
4
5
6
7
8
9
use std::fmt;

type Wrapper = Vec<String>;

impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.join(", "))
}
}