1.16.1. 让特征泛型的两种方式

特征可以通过两种方式实现泛型:

  • 泛型类型参数。例如:trait Foo<T>
  • 关联类型。例如:trait Foo { type Bar; }

二者的区别在于:

  • 对于关联类型,特定类型的给定特征只有一种实现
  • 对于泛型参数,则可以存在多种实现

简单建议:如果可能,优先使用关联类型。

1.16.2. 泛型(类型参数)特征

泛型特征要求你指定所有泛型类型参数并重复它们的约束

这在维护上会更困难。例如,如果你向特征添加一个泛型类型参数,该特征的所有实现者都必须更新他们的代码。

这种形式还可能导致一个特征对给定类型存在多个实现的问题。此时编译器更难推断出你实际想要的特征实例。有时你必须调用消除歧义的函数,例如FromIterator::<u32>::from_iter

在某些情况下,此特性也是一个优势,例如:

  • impl PartialEq<BookFormat> for Book,其中BookFormat可以是不同类型
  • 你可以同时实现FromIterator<T>FromIterator<&T> where T: Clone

1.16.3. 关联类型特征

我们用一段代码作为示例:

trait Contains {
    type A;
    type B;

    // 更新语法以泛型方式引用这些新类型
    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}

进入全屏模式 退出全屏模式

使用关联类型时:

  • 编译器只需要知道实现该特征的类型
  • 约束可以完全放在特征本身上,无需重复
  • 将来添加另一个关联类型不会影响用户
  • 具体类型决定了特征内的关联类型,因此无需使用消除歧义的函数。看这个示例:
impl Contains for Container {
    // 指定`A`和`B`的类型。如果`input`类型
    // 是`Container(i32, i32)`,则`output`类型被确定
    // 为`i32`和`i32`。
    type A = i32;
    type B = i32;

    // 这里`&Self::A`和`&Self::B`同样有效。
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }

    // 获取第一个数字。
    fn first(&self) -> i32 { self.0 }

    // 获取最后一个数字。
    fn last(&self) -> i32 { self.1 }
}

进入全屏模式 退出全屏模式

  • 此示例为Container类型实现了Contains特征
  • ContainerContains实现中的关联类型由type A = i32;type B = i32;确定

不能为多个目标类型实现 Deref

查看Deref特征的源代码:

pub trait Deref {
    type Target: ?Sized;

    fn deref(&self) -> &Self::Target;
}

进入全屏模式 退出全屏模式

  • type Target: ?Sized;中的Target是我们所说的目标类型

我们编写一个Deref实现来说明:

use std::ops::Deref;

struct Wrapper {
    value: String,
}

impl Deref for Wrapper {
    type Target = String;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

进入全屏模式 退出全屏模式

在上面的代码中,Wrapper只能被解引用为String。但如果你想让Wrapper同时被解引用为Stringstr,Rust允许你再次实现Deref,因为Target只能有一个具体类型。

换句话说,这是非法的:

use std::ops::Deref;

struct Wrapper {
    value: String,
}

// 第一个Deref实现,Target = String
impl Deref for Wrapper {
    type Target = String;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

// 这是非法的:Rust不允许为同一个`Wrapper`类型实现第二个`Deref`
impl Deref for Wrapper {
    type Target = str;  // 冲突:Rust无法推断哪个`Target`适用

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

进入全屏模式 退出全屏模式


不能使用多个 Item 来实现 Iterator 特征

原因与不能为多个目标类型实现Deref的原因相同:主要涉及关联类型的唯一性和 Rust 编译器的推断规则