在阅读本部分内容的同时,你可能会好奇使用泛型类型参数是否会有运行时消耗。好消息是:Rust 实现了泛型,使得使用泛型类型参数的代码相比使用具体类型并没有任何速度上的损失。
Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。
编译器寻找所有泛型代码被调用位置并使用泛型代码针对具体类型生成代码。让我们看看一个使用标准库中 Option 枚举的例子:
let float = Some(5.0);
当 Rust 编译这些代码的时候,它会进行单态化。编译器会读取传递给 Option 的值并发现有两种Option:一个对应 i32 另一个对应 f64。为此,它会将泛型定义 Option 展开为 Option_i32 和Option_f64,接着将泛型定义替换为这两个具体的定义。
编译器生成的单态化版本的代码看起来像这样,并包含将泛型 Option 替换为编译器创建的具体定义后的用例代码:
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
}
我们可以使用泛型来编写不重复的代码,而 Rust 将会为每一个实例编译其特定类型的代码。这意味着在使用泛型时没有运行时开销;当代码运行,它的执行效率就跟好像手写每个具体定义的重复代码一样。这个单态化过程正是 Rust 泛型在运行时极其高效的原因。
trait:定义共享的行为
commit 3c2ca8528c3b92b7d30e73f2e8a1b84b2f68b0c8
trait 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为。可以使用 trait bounds 指定泛型是任何拥有特定行为的类型。
定义 trait
一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。
例如,这里有多个存放了不同类型和属性文本的结构体:结构体 NewsArticle 用于存放发生于世界各地的新闻故事,而结构体 Tweet 最多只能存放 280 个字符的内容,以及像是否转推或是否是对推友的回复这样的元数据。
我们想要创建一个名为 aggregator 的多媒体聚合库用来显示可能储存在 NewsArticle 或 Tweet 实例中的数据的总结。每一个结构体都需要的行为是他们是能够被总结的,这样的话就可以调用实例的summarize 方法来请求总结。一个表现这个概念的公有 Summary trait 的定义:
fn summarize(&self) -> String;
}
这里使用 trait 关键字来声明一个 trait,后面是 trait 的名字,在这个例子中是 Summary。我们也声明 trait 为 pub 以便依赖这个 crate 的 crate 也可以使用这个 trait,正如我们见过的一些示例一样。在大括号中声明描述实现这个 trait 的类型所需要的行为的方法签名,在这个例子中是fn summarize(&self) −> String。
在方法签名后跟分号,而不是在大括号中提供其实现。接着每一个实现这个 trait 的类型都需要提供其自定义行为的方法体,编译器也会确保任何实现 Summary trait 的类型都拥有与这个签名的定义完全一致的 summarize 方法。
trait 体中可以有多个方法:一行一个方法签名且都以分号结尾。