【Rust 中级教程】 04 trait (2)

简介: 【Rust 中级教程】 04 trait (2)

定义泛型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 名称后面添加 <T> 符号则标明该 trait 是一个泛型的 trait。我们声明了一个结构体 Test,为其实现了 MyPrint<i32> 的trait。最后,我们就可以调用 print 直接输输出 i32 类型的数据了 。像类似 MyPrint<i32> 、 MyPrint<String> 、 MyPrint<u8>等等这都不是同一类型的。


泛型约束



我们还可以使用 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 是首字母大写的。我们先看下官方代码示例。下面内容可以仅作为了解。


0a2653c851af460fa595bd959398a8f1.png


这是官方源码中的 Clone trait,clone 方法返回了 Self。clone 方法的返回类型就是自身类型, 这也就意味着 Self 其实就是自身类型。我们再进一步剖析它。我这里随便点了几个实现,下面源码来自 client.rs


2d65d23f6d4748949b924e4057485923.png


我们从这两个 impl 代码块中,可以清晰的看出,其实 Self 其实就是当前类型的别名。第一个 impl 块中,Self 是 Group 的别名,在第二个 impl 块中,Self 是 Literal 的别名。


0x04 小结


本篇文章着重介绍了泛型与 trait 结合使用的场景。这也是应对复杂场景的一种解决办法,也是我们必须要掌握的重点。有关 trait 的相关知识还有很多,下一篇文章依然还是 trait。

相关文章
|
2月前
|
存储 Rust
30天拿下Rust之Trait
30天拿下Rust之Trait
47 0
|
4月前
|
Rust
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
|
Rust 安全 JavaScript
Rust教程初识
当然,它们都有编译然后执行可执行程序的用法。应当有编译后改名的方式,不然那也太不coooooooooooool了。
78 0
|
存储 消息中间件 Rust
Rust极简教程
Rust是一门赋予每个人构建可靠且高效软件能力的编程语言。可靠主要体现在安全性上。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软件。
320 0
Rust极简教程
|
Rust 安全 Java
Rust学习笔记之泛型、trait 与生命周期
1. 泛型大补汤 推荐阅读指数 ⭐️⭐️⭐️⭐️ 2. 泛型数据类型 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 3. trait:定义共享的行为 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 4. 生命周期与引用有效性 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
105 0
Rust学习笔记之泛型、trait 与生命周期
|
Rust 编译器
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)
|
Rust 编译器
Rust 中级教程 第16课——引用的 lifetime(1)
Rust 中级教程 第16课——引用的 lifetime(1)
Rust 中级教程 第16课——引用的 lifetime(1)
|
Rust C++
【Rust 中级教程】 15 引用与借用(3)
【Rust 中级教程】 15 引用与借用(3)
【Rust 中级教程】 15 引用与借用(3)
|
Rust 编译器
【Rust 中级教程】 13 引用与借用(1)
【Rust 中级教程】 13 引用与借用(1)
【Rust 中级教程】 13 引用与借用(1)
|
Rust 安全 编译器
【Rust 中级教程】 12 共享所有权
【Rust 中级教程】 12 共享所有权
【Rust 中级教程】 12 共享所有权