【Rust 中级教程】 03 trait (1)

简介: 【Rust 中级教程】 03 trait (1)

0x00 开篇


前面用两篇文章介绍了泛型,第二课也算是对结构体的一个补充了。结构体的知识尤为重要,今天这篇文章依然是围绕结构体来做介绍。相信有其它面向对象编程语言基础的小伙伴都了解类和接口的概念。但是 Rust 没有类和接口,那么它又是如何实现面向对象特征的呢?


0x01 trait定义


trait 是 Rust 中唯一的接口抽象方式,类似于 Java 和 C# 的 interface。trait 可以定义多个抽象的方法(行为)来用于多个结构体共享,使得不同的结构体可以拥有相同的方法(行为)。在某些博客或者书籍上会将 trait 翻译为 特型/特性/特征。在后面的文章中,我不会将其翻译成中文。


0x02 trait的实现


trait 是一种任何类型都可以选择支持或者不支持的特性,通常代表某一种行为或者能力。

示例:所有的动物基本上都会”说话“或者有一些其它类似的行为,但是它们的叫声又有区别,我们可以为把他们抽象出一个 trait 叫 Animal

示例代码如下:

/// 动物 trait
trait Animal {
    // 动物的叫声
    fn make_sound(&self);
}

上面的代码就是定义了一个动物的 trait,定义 trait 很简单,我们只需要给它命名并且列出 trait 方法的类型签名即可。那我们如何使用这个 trait 呢?实现这个 trait 的语法如下:

impl [TraitName] for [Type]

在 Rust 中,我们可以使用泛型为任何类型甚至是 str , i32,  bool 等内置类型添加扩展方法。这里声明的 Animal 属于动物,我再定义几个动物类型的结构体。通常 trait 都是与 struct 一起使用。完整代码如下:

/// 动物 trait
trait Animal {
    // 动物的叫声
    fn make_sound(&self);
}
/// 狗
struct Dog {
    name: String,
}
/// 鱼
struct Cat {
    name: String,
}
/// 为 Dog 类型实现 Animal trait
impl Animal for Dog {
    // 打印狗的叫声
    fn make_sound(&self) {
        println!("汪汪~");
    }
}
/// 为 Cat 类型实现 Animal trait
impl Animal for Cat {
    // 打印猫的叫声
    fn make_sound(&self) {
        println!("喵喵~");
    }
}
fn main() {
    // 创建dog
    let dog = Dog { name: String::from("二哈") };
    // 创建cat
    let cat = Cat { name: String::from("美短") };
    // 运行方法
    dog.make_sound();
    cat.make_sound();
}
    // 运行结果:
    // 汪汪~
    // 喵喵~

上面代码注释都很明确,我就不多做解释了。使用 trait 的方法时候有两个注意事项:

  • 定义 trait 方法的时候不要忘记第一个参数必须是 &self。有没有 &self 的区别,上一篇文章我已经解释了。
  • 使用 trait 方法的时候,trait 本身必须在当前作用域中,否则 trait 所有的方法都是隐藏的。这一点,CLion已经给了我们很好的提示。(有关示例代码我将在源码中给出,由于还没有讲到模块化,所以这里暂不多解释)

0x03 impl trait


trait 作为参数时一般使用 impl trait 的语法来表示参数类型。先上代码:

/// impl trait 作为参数
fn speak(animal: impl Animal) {
    animal.make_sound()
}
/// 为已有类型实现 trait (示例)
impl Animal for i32 {
    fn make_sound(&self) {
        println!("i32");
    }
}
fn main() {
    speak(dog);
    speak(cat);
    let a = 5;
    speak(a);
    // 运行结果:
    // 汪汪~
    // 喵喵~
    // i32
}

speak 函数表示可以接收实现了 Animal trait 的任何实例,包括已经存在的类型,上面代码以 i32 为例。如果你想让 speak 函数接收任意类型,那你可以让那个类型实现   Animal trait。

问题又来了,如果我想在某一个函数里接收同时实现了多个 trait 的参数应该如何操作呢?继续往下看:

/// 测试多trait
trait Test {
    fn test(&self);
}
/// 为Dog实现Test
impl Test for Dog {
    fn test(&self) {
        println!("这是一个Test方法");
    }
}
/// 打印同时实现 Test 和 Animal Trait的方法
fn printMulti(p: impl Test + Animal) {
    p.make_sound();
    p.test();
}
fn main() {
    // 多 trait同时实现
    printMulti(dog);
    // 下面的代码错误
    // printMulti(cat);
    // 运行结果:
    // 汪汪~
  // 这是一个Test方法
}

上面的代码又定义了一个 Test 的 trait,我们也为 Dog  实现了这个 Test。最后定义了一个 printMulti 函数打印内容。我们不能为这个函数传参 cat,因为它没有实现 Test。运行后,编译器会抛出下面的错误。


0a2653c851af460fa595bd959398a8f1.png


0x04 小结


本篇文章简单介绍了 trait 的概念和基础用法。如果你有 JavaC# 等语言基础,那么这篇文章你将很容易理解。如果你有 CC# 语言基础,你会发现 trait 的方法很像虚函数。有没有感觉 Rust 有将其它语言结合的特点呢。由于本篇文章所涉及的代码比较多,所以就先介绍这些吧。下一篇文章继续来介绍 trait 的其它内容。

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