Skip to main content
18 高级trait

18 高级trait

•1 min•129 words

关联类型

关联类型是用于帮助trait设计者描述类型限制的工具

trait设计的时候,就需要保证trait中的方法满足trait所要实现的功能。这个时候为了防止trait实现者胡乱实现,就可以用关联类型来限制trait的胡乱实现。要求实现者满足关联类型的限制要求。

关联类型的定义: type 关联类型名;

在trait 内部,通过type 关键字 接 关联类型的占位符命名即可。

Summary

Self 是指“实现了当前这个 trait 的那个具体类型”。它可以是结构体、枚举、联合体、基本类型(如 i32),甚至是引用或元组。

trait的实现中,指定关联类型

在具体的实现中,需要指明关联类型的具体类型。

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        // --snip--
    }
}
为什么不像泛型那样对每个不同关联类型的trait通过来区别?

[!faq] 为什么不像泛型那样对每个不同关联类型的trait通过<具体类型>来区别?
因为每个类型只能实现1次trait,所以对于这个类型来说,只有1种trait,所以根本不需要区分不同的trait实现,只有我自己的一种实现。

泛型+trait

泛型技术的本质:编译器函数

泛型在rust中是单态化的,也就是编译器会将泛型最终转换成具体类型。所以泛型帮助我们使用类型参数生成不同类型参数相似的类型实现。

Summary

诸如此类还有很多,不如直接 厘米运算<类型参数> trait,我们想要哪个trait填参数。

use std::ops::Add;

struct Millimeters(u32);
struct Meters(u32);

impl Add<Meters> for Millimeters {
    type Output = Millimeters;

    fn add(self, other: Meters) -> Millimeters {
        Millimeters(self.0 + (other.0 * 1000))
    }
}

默认类型参数

在定义类型参数的时候,设置默认值

有时候,某个类型参数使用的特别多,我们将其设置为默认类型参数,这样实现者完全可以忽略泛型。

trait Add<Rhs=Self> {
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}

默认参数类型主要用于如下两个方面:

  • 扩展类型而不破坏现有代码。
  • 在大部分用户都不需要的特定情况进行自定义。

运算符重载

运算符是泛型+trait技术的典型用例

Rust 并不允许创建自定义运算符或重载任意运算符,但可以通过实现std::ops 中列出的运算符相关 trait 来重载它们。(因为本质上运算符就是通过寻找类型的运算符trait来实现功能的)

Summary
  1. 运算符类型,比如+运算符就是 std::ops::Add;
  2. 参数类型,比如 Self + Self 是默认情况, Self + 另外的类型 就 std::ops::Add<另外的类型>;

各trait中的同名方法

Faq
  1. 可以通过 trait::method_name(传入变量)来调用方法,trait能通过传入self的类型来判断具体要调用trait为哪个结构体的实现。(但是如果不是方法,就没有办法判断了)
  2. 使用完全限定语法, <Dog as Animal>::baby_name() 这里从语法上非常像将结构体转型成了trait object类型,但是这里发生的是静态的,不会转换成trait object这样会有性能消耗。

我们在尖括号中向 Rust 提供了类型注解,这表明我们希望在此次函数调用中将 Dog 类型视为 Animal,从而调用在 Dog 上实现的 Animal trait 中的 baby_name 方法。

通常,完全限定语法定义为如下:

<Type as Trait>::function(receiver_if_method, next_arg, ...);

超trait

Summary

语法就是在trait定义添加trait约束。

use std::fmt;

trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        let output = self.to_string();
        let len = output.len();
        println!("{}", "*".repeat(len + 4));
        println!("*{}*", " ".repeat(len + 2));
        println!("* {output} *");
        println!("*{}*", " ".repeat(len + 2));
        println!("{}", "*".repeat(len + 4));
    }
}

使用 newtype 模式在外部类型上实现外部 trait

Summary

如果希望新类型拥有其内部类型的每一个方法,为封装类型实现 Deref trait并返回其内部类型是一种解决方案。