0x00 开篇
今天是中秋节,是除了春节外最重大的节日,在这里我先祝大家中秋节快乐。话不多说,本篇文章继续来介绍 trait。
0x01 泛型与 trait
定义泛型trait
泛型它又来了,泛型和 trait 又会发生什么样的火花呢?先看下面的代码:
// 定义一个泛型trait
trait MyPrint<T> {
// 输出传递的参数
fn print(&self, x: T) -> T;
}
// 测试结构体
struct Test;
// 为Test实现MyPrint
impl MyPrint<i32> for Test {
// 返回值
fn print(&self, x: i32) -> i32 {
return x;
}
}
fn main() {
let test = Test;
// 直接输出结果
println!("{}", test.print(3));
}
MyPrint 是一个泛型 trait,在 trait 名称后面添加 符号则标明该 trait 是一个泛型的 trait。我们声明了一个结构体 Test,为其实现了 MyPrint 的trait。最后,我们就可以调用 print
直接输输出 i32 类型的数据了 。像类似 MyPrint 、 MyPrint 、 MyPrint等等这都不是同一类型的。
泛型约束
我们还可以使用 trait 对泛型进行约束,trait 约束与泛型参数声明在一起,这种方式一般用于复杂的开发场景。示例代码如下:
fn trait_demo<T: TraitOne + TraitTwo + TraitOther>(param: T)
{
// code...
}
这里的 T 表示该参数同时实现了 TraitOne, TraitTwo, TraitOther 三个 trait。
如果存在多个泛型,我们也可以对多个泛型进行约束。示例如下:
fn multi_fun<T: TraitOne, E: TraitTwo + TraitOther>(param1: T, param2: E) {
// code...
}
在遇到更加复杂的开发场景时,可能存在泛型存在多个 trait 约束的场景,为了避免可读性差和“头重脚轻”的问题,我们可以通过 where 关键字来进行优化。可以将约束写在函数签名后面——where + 约束条件。
以 multi_fun 为例,转为 where 关键字的写法如下:
fn multi_fun_where<T,E>(param1: T, param2: E) where T: TraitOne, E: TraitTwo + TraitOther {
// code...
}
由于示例代码过多,这里我就不再贴代码占用空间了,详细示例代码请点击文末 阅读原文下载。
PS:如果这里有读者读过的张汉东老版本编著的《Rust编程之道》,请不要被书中的 trait 数学的集合概念所误导,该篇内容讲述的逻辑和概念是错误的。作者回应将在第二版第4次印刷修改。附原文地址:https://github.com/ZhangHanDong/tao-of-rust-codes/issues/99
0x02 Supertraits
Rust 中没有 “继承”的概念,但是我们可以定义一个 trait 为另一个 trait 的超集。在某些文章中又叫做”子 trait“ 或者”继承“。
// Supertraits
trait Animal {
fn speak(&self);
}
trait Dog: Animal {
// 狗还会跳
fn jump(&self);
}
struct SmallDog;
// 为 SmallDog 实现 Dog 的同时,也必须实现 Animal
impl Animal for SmallDog {
fn speak(&self) {
}
}
impl Dog for SmallDog {
fn jump(&self) {
}
}
我们再为 SmallDog 实现 Dog 的同时,也要为其实现 Animal。实现的顺序是无所谓的,但必须要实现,否则会产生错误。其实这点类似于 Java 中的接口继承。
0x03 trait 中的 Self
在 trait 中,我们可以使用关键字 Self 作为类型。这里的 Self 是首字母大写的。我们先看下官方代码示例。下面内容可以仅作为了解。
这是官方源码中的 Clone trait,clone 方法返回了 Self。clone 方法的返回类型就是自身类型, 这也就意味着 Self 其实就是自身类型。我们再进一步剖析它。我这里随便点了几个实现,下面源码来自 client.rs
。
我们从这两个 impl 代码块中,可以清晰的看出,其实 Self 其实就是当前类型的别名。第一个 impl 块中,Self 是 Group 的别名,在第二个 impl 块中,Self 是 Literal 的别名。
0x04 小结
本篇文章着重介绍了泛型与 trait 结合使用的场景。这也是应对复杂场景的一种解决办法,也是我们必须要掌握的重点。有关 trait 的相关知识还有很多,下一篇文章依然还是 trait。