掌握Rust Trait:灵活和模块化代码的关键

娜美-弗兰奇-乌索普

计算机科学家倾向于处理非统一性结构(情形 1、情形 2、情形 3),而数学家则倾向于找一个统一的公理来管理整个体系。

------高德纳

特型 (Trait)

特型 (trait) 是 Rust 体系中的接口或抽象基类。类似于其他语言中的接口(interfaces),允许你定义一组方法签名,这些方法可以在不同的类型(structs、enums等)上实现。trait的使用提高了代码的模块化和复用性,同时也是Rust多态性的基础

定义特型 (Trait)

定义trait时,只需列出方法签名,而不需要提供方法体。这些方法可以在不同的类型上具体实现

rust 复制代码
trait Speak {
    fn speak(&self);
}

实现特型 (Trait)

使用impl关键字,为特定类型实现trait

rust 复制代码
struct Dog;

impl Speak for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

使用特型 (Trait)

一旦为类型实现了trait,就可以调用该trait中定义的方法了。这允许编写操作抽象trait对象的泛型函数或方法,而不是具体类型

rust 复制代码
fn call_speak(speaker: &impl Speak) {
    speaker.speak();
}

// 或使用泛型和Trait Bound
fn call_speak<T: Speak>(speaker: &T) {
    speaker.speak();
}

默认实现

trait还可以提供方法的默认实现。如果一个类型实现了该trait但没有为某个方法提供具体实现,则会使用默认实现

rust 复制代码
trait Speak {
    fn speak(&self) {
        println!("I cannot speak.");
    }
}

标准库中对默认方法最引人注目的应用场景是 Iterator 特型,它有一个必要方法 (.next()) 和几十个默认方法

作为参数的特型 (Trait)

trait可以作为函数的参数,这允许编写可以接受任何实现了特定trait的类型的函数。这种特性使Rust的类型系统更加灵活,编写更通用的代码

如下trait示例

rust 复制代码
trait Animal {
    fn speak(&self);
}

编写一个函数,这个函数接受一个实现了Animal trait的类型的引用

rust 复制代码
fn call_speak(animal: &impl Animal) {
    animal.speak();
}

作为参数的特型 (Trait) 方法

当有一个实现了trait的类型的引用时,可以调用trait中定义的方法。这些方法的具体实现是在运行时选择的

例如:一个Dog类型,这个类型实现了Animal trait

rust 复制代码
struct Dog;

impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

创建一个Dog类型的实例,然后调用speak方法

rust 复制代码
fn main() {
    let dog = Dog;
    call_speak(&dog);
}

特型 (Trait) 继承

trait可以继承其他trait。当实现一个trait时,也必须为所有它继承的trait提供实现

rust 复制代码
trait Animal {
    fn name(&self) -> String;
}

trait Speak: Animal {
    fn speak(&self) {
        println!("{} cannot speak.", self.name());
    }
}

作为返回类型的特型 (Trait)

将trait作为函数的返回类型时,可以返回实现了trait的类型的实例。这允许编写函数,这些函数返回抽象trait对象,而不是具体类型

如下例子

rust 复制代码
struct Dog;

trait Animal {
    fn speak(&self);
}


impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

fn create_dog() -> impl Animal {
    Dog
}

fn main() {
    let dog = create_dog();
    dog.speak();
}

孤儿规则

孤儿规则(Orphan Rule)是一种类型系统规则,用于确保类型实现的trait只有一个来源。这个来源可以是trait自身的定义,也可以是类型的定义

孤儿规则规定:

  • 如果一个trait是在当前crate中定义的,那么任何crate中的类型都可以为这个trait提供实现

  • 如果一个trait是在外部crate中定义的,只有当类型或trait都是在当前crate中定义的时候,才可以为这个trait提供实现

这个规则的目的是确保trait的实现不会影响到其他crate。如果没有这个规则,那么当一个crate为一个外部trait提供了实现,那么其他crate也可能会为相同的trait提供实现,这可能会导致冲突和不一致

总结

trait是Rust中实现多态和代码复用的强大工具。通过定义共享的行为接口,trait允许不同的类型以一致的方式被处理。此外,trait的灵活性还体现在可以作为参数传递、可以被其他trait继承,以及可以提供默认实现等特性

欢迎大家讨论交流,如果喜欢本文章或感觉文章有用,动动你那发财的小手点赞、收藏、关注再走呗 ^_^

微信公众号:草帽Lufei

相关推荐
沐森1 分钟前
Rust 的CPU和IO操作
后端
Lucky_Turtle4 分钟前
【Springboot】解决PageHelper在实体转Vo下出现total数据问题
java·spring boot·后端
無量8 分钟前
AI工程化实践指南:从入门到落地
后端·ai编程
golang学习记9 分钟前
Jetbrains 这个知名软件十年了!
后端
韭菜炒大葱11 分钟前
现代前端开发工程化:Vue3 + Vite 带你从 0 到 1 搭建 Vue3 项目🚀
前端·vue.js·vite
老华带你飞11 分钟前
志愿者服务管理|基于springboot 志愿者服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
知其然亦知其所以然14 分钟前
程序员的最强外挂:用 Spring AI 解锁智谱 AI 画图能力
后端·spring·程序员
栀秋66619 分钟前
面试常考的最长递增子序列(LIS),到底该怎么想、怎么写?
前端·javascript·算法
Melrose20 分钟前
Flutter - 使用Jaspr来构建SEO友好网站
前端·flutter
有点笨的蛋22 分钟前
Vue3 项目:宠物照片变身冰球运动员的 AI 应用
前端·vue.js