09-特征的多种形式
4.3.2 特征的多种形式
在前面的示例中,我们了解了最简单的特征形式,但这只是特征的冰山一角。当你开始接触大型代码库中的特征时,将会遇到它的多种形式。因为程序的复杂度和要解决的问题相比较,简单的特征形式可能并不适合我们使用。Rust为我们提供了其他形式的特征,可以很好地帮助我们为问题建模。我们将介绍一个标准库的特征,并尝试对它们进行分类,以便了解何时需要使用它们。
标记特征
在std::marker模块中定义的特征被称为标记特征(marker trait)。这种特征不包含任何方法,声明时只是提供特征名称和空的函数体。
标准库中的示例包括Copy、Send、Sync。它们被称为标记特征,因为它们用于简单地将类型标记为属于特定的组群,以获得一定程度的编译期保障。标准库中的两个这样的示例是Send和Sync特征,它们在适当的时候由语言为大多数类型自动实现,并确定哪些值可以安全地发送和跨线程共享。我们将在第8章对它们进行详细介绍。
简单特征
这可能是特征定义的最简单形式。我们已将它作为特征的简单定义进行了阐述:
trait Foo {
fn foo();
}
标准库中的一个示例是Default特征,它主要是针对可以使用默认值初始化的类型实现的。
泛型特征
特征也可以是泛型。这在用户希望为多种类型实现特征的情况下非常有用:
pub trait From<T> {
fn from(T) -> Self;
}
这样的两个例子是From
关联类型特征
trait Foo {
type Out;
fn get_value(self) -> Self::Out;
}
这是泛型特征的更好选择,因为它们能够在特征中声明相关类型,例如前面代码中特征Foo声明的Out类型。它们具有较少的类型签名,其优点在于,在实际的编程中,它们允许用户一次性声明关联类型,并在任何特征方法或函数中使用Self::Out作为返回类型或参数类型。这消除了类型的冗余声明,与泛型特征的情况类似。关联类型特征的最佳用例之一是Iterator特征,它用于迭代自定义类型的值。在第8章中,我们将会深入介绍迭代器。
继承特征
我们已经在代码示例trait_inheritance.rs中看到了这些特征。与Rust中的类型不同,特征可以具有继承关系,例如:
trait Bar {
fn bar();
}
trait Foo: Bar {
fn foo();
}
在上述代码片段中,我们声明了一个特征Foo,它依赖于父级特征Bar。在Foo的定义中,要求用户在为类型实现Foo特征时必须为Bar特征提供实现。标准库中的这样一个示例是Copy特征,它要求类型必须实现Clone特征。