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


    图片

    0x00 开篇

    今天是中秋节,是除了春节外最重大的节日,在这里我先祝大家中秋节快乐。话不多说,本篇文章继续来介绍 trait

    0x01 泛型与 trait

    定义泛型trait

    图片

    泛型它又来了,泛型和 trait 又会发生什么样的火花呢?先看下面的代码:

    // 定义一个泛型trait
    trait MyPrint {
        // 输出传递的参数
        fn print(&self, x: T) -> T;
    }
    
    
    // 测试结构体
    struct Test;
    
    
    // 为Test实现MyPrint
    impl MyPrint for Test {
    
    
        // 返回值
        fn print(&self, x: i32) -> i32 {
            return x;
        }
    }
    
    
    
    
    fn main() {
        let test = Test;
        // 直接输出结果
        println!("{}", test.print(3));
    }
    
    
    • 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

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

    泛型约束

    图片

    我们还可以使用 trait 对泛型进行约束,trait 约束与泛型参数声明在一起,这种方式一般用于复杂的开发场景。示例代码如下:

        fn trait_demo(param: T)
    {
        // code...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里的 T 表示该参数同时实现了 TraitOne, TraitTwo, TraitOther 三个 trait。

    如果存在多个泛型,我们也可以对多个泛型进行约束。示例如下:

    fn multi_fun(param1: T, param2: E) {
        // code...
    }
    
    
    • 1
    • 2
    • 3
    • 4

    在遇到更加复杂的开发场景时,可能存在泛型存在多个 trait 约束的场景,为了避免可读性差和“头重脚轻”的问题,我们可以通过 where 关键字来进行优化。可以将约束写在函数签名后面——where + 约束条件。

    multi_fun 为例,转为 where 关键字的写法如下:

    fn multi_fun_where(param1: T, param2: E) where T: TraitOne, E: TraitTwo + TraitOther {
        // code...
    }
    
    
    • 1
    • 2
    • 3
    • 4

    由于示例代码过多,这里我就不再贴代码占用空间了,详细示例代码请点击文末 阅读原文下载。

    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) {
        }
    }
    
    
    • 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

    我们再为 SmallDog 实现 Dog 的同时,也要为其实现 Animal。实现的顺序是无所谓的,但必须要实现,否则会产生错误。其实这点类似于 Java 中的接口继承。

    0x03 trait 中的 Self

    在 trait 中,我们可以使用关键字 Self 作为类型。这里的 Self 是首字母大写的。我们先看下官方代码示例。下面内容可以仅作为了解。

    图片

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

    图片

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

    0x04 小结

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

  • 相关阅读:
    Python:实现merge insertion sort合并插入排序算法(附完整源码)
    Java数组与八大排序
    Nginx:动静分离(示意图+配置讲解)
    商品换购小程序
    关于electron打包卡在winCodeSign下载问题
    Linux常用命令——cp命令
    Git要提交到B分支。但是误提交到A分支,然后推送到远程仓库。然后撤回并重新提交到B分支。
    Qt使用QListWidget实现自定义Item效果
    AtCoder Beginner Contest 278「A」「B」「C」「D」「E」「F 对抗博弈」
    搭建大型分布式服务(四十)SpringBoot 整合多个kafka数据源-支持生产者
  • 原文地址:https://blog.csdn.net/a1595901624/article/details/126795625