mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 04:33:27 +08:00
Updated src/generic_types_traits_and_lifetimes/generics.md'.
This commit is contained in:
@@ -1,22 +1,23 @@
|
||||
struct Point<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
#[derive(Debug)]
|
||||
struct Point<X1, Y1> {
|
||||
x: X1,
|
||||
y: Y1,
|
||||
}
|
||||
|
||||
impl<T> Point<T> {
|
||||
fn x(&self) -> &T {
|
||||
&self.x
|
||||
}
|
||||
}
|
||||
|
||||
impl Point<f32, f32> {
|
||||
fn distance_from_origin(&self) -> f32 {
|
||||
(self.x.powi(2) + self.y.powi(2)).sqrt()
|
||||
impl<X1, Y1> Point<X1, Y1> {
|
||||
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
|
||||
Point {
|
||||
x: self.x,
|
||||
y: other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let p = Point { x: 5, y: 10 };
|
||||
let p1 = Point { x: 5, y: 10.4 };
|
||||
let p2 = Point { x: "Hello", y: 'c' };
|
||||
|
||||
println!("p.x = {}", p.x());
|
||||
let p3 = p1.mixup(p2);
|
||||
|
||||
println! ("p3.x = {}, p3.y = {}", p3.x, p3.y);
|
||||
}
|
||||
|
||||
@@ -279,21 +279,20 @@ impl Point<f32, f32> {
|
||||
|
||||
这段代码表示类型 `Option<f32>` 将有着 `distance_from_origin` 方法;而其中 `T` 不是 `f32` 的其他 `Option<T>` 实例将不会被定义这个方法。该方法测量我们的点距坐标 `(0.0, 0.0)` 处点的有多远,并使用了仅对浮点类型可用的数学运算。
|
||||
|
||||
结构体定义中的泛型类型参数,并不总与咱们在同一结构体的方法签名中使用的泛型类型参数相同。为让示例更明确,下面清单 10-11 对 `Point` 结构体,使用了泛型 `T` 与 `U`,而对 `mixup` 方法签名则使用了 `X` `Y`。
|
||||
这个方法使用来自 `self` `Point` 的 `x` 值(类型为 `T`),与来自传入的那个 `Point` 值的 `y` (类型为 `Y`),创建出一个新的 `Point`。
|
||||
结构体定义中的泛型类型参数,并不总与咱们在同一结构体的方法签名中使用的泛型类型参数相同。下面清单 10-11 对 `Point` 结构体使用泛型类型 `X1` 与 `Y1`,而对 `mixup` 方法签名使用 `X2` 和 `Y2`,以使示例更清晰。这个方法以 `self` `Point` 中的 `x` 值(类型为 `X1`)与传入的 `Point` 中的 `y` (类型为 `Y2`),创建一个新的 `Point` 实例。
|
||||
|
||||
<a name="listing_10-11"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Point<T, U> {
|
||||
x: T,
|
||||
y: U,
|
||||
struct Point<X1, Y1> {
|
||||
x: X1,
|
||||
y: Y1,
|
||||
}
|
||||
|
||||
impl<T, U> Point<T, U> {
|
||||
fn mixup<X, Y>(self, other: Point<X, Y>) -> Point<T, Y> {
|
||||
impl<X1, Y1> Point<X1, Y1> {
|
||||
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
|
||||
Point {
|
||||
x: self.x,
|
||||
y: other.y,
|
||||
@@ -311,32 +310,33 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 10-11:一个使用了与其结构体定义不同泛型的方法*
|
||||
**清单 10-11**:使用与其结构体定义不同的泛型类型的方法
|
||||
|
||||
在 `main` 函数中,咱们定义了一个有着 `x` 为 `i32` (值为 `5`),及 `y` 为 `f64` (值为 `10.4`)的 `Point`。变量 `p2` 是个有着 `x` 为字符串切片(值为 `Hello`),同时 `y` 为 `char` (值为 `c`)的 `Point` 结构体。以参数 `p2` 调用 `p1` 上的 `mixup`,就给到咱们 `p3`,由于 `p3` 的 `x` 来自于 `p1`,因此将有一个 `i32` 的 `x`。而由于这个变量 `p3` 的 `y` 来自于 `p2`, 因此他将有一个 `char` 的 `y`。那个 `println!` 宏调用,将打印 `p3.x = 5, p3.y = c`。
|
||||
在 `main` 函数中,咱们定义了个 `Point`,他有着 `i32` 的 `x`(值为 `5`),及 `f64` 的 `y`(值为 `10.4`)。变量 `p2` 是个 `Point` 结构体,有着字符串切片的 `x`(值为 `Hello`)和 `char` 的 `y`(值为 `c`)。以参数 `p2` 对 `p1` 调用 `mixup` 给到我们 `p3`,其将有着 `i32` 的 `x`,因为 `x` 来自 `p1`。变量 `p3` 将有个 `char` 的 `y`,因为 `y` 来自 `p2`。`println!` 宏调用将打印 `p3.x = 5, p3.y = c`。
|
||||
|
||||
此示例的目的,是要对其中有些泛型参数是以 `impl` 来声明,而另一些泛型参数则是以方法定义来声明的情形,加以演示。由于这里的泛型参数 `T` 与 `U` 与结构体定义在一起,因此他们是在 `impl` 后声明的。而其中的泛型参数 `X` 与 `Y`,则由于他们只与方法 `mixup` 有关,所以他们就被声明在了 `fn mixup` 之后。
|
||||
这个示例的目的是演示其中一些泛型参数以 `impl` 声明,而另一些以方法定义声明的情形。这里,泛型参数 `X1` 和 `Y1` 声明于 `imple` 之后,因为他们与结构体的定义有关。泛型参数 `X2` 与 `Y2` 声明于 `fn mixup` 之后,因为他们仅与该方法有关。
|
||||
|
||||
|
||||
## 用到泛型代码的性能问题
|
||||
## 用到泛型的代码的性能问题
|
||||
|
||||
**Performance of Code Using Generics**
|
||||
咱们可能想知道使用泛型类型参数时,是否存在运行时开销。好消息就是,使用泛型类型将不会使咱们的程序运行得比其在具体类型下更慢。
|
||||
|
||||
Rust 通过在编译时对使用泛型的代码执行单态化达成这点。所谓 *单态化,monomorphization* <sup>1</sup>,是指通过在编译时填入用到的具体类型,从而将泛型代码转换为具体代码的过程。在这个过程中,编译器执行与 [清单 10-5](#listing_10-5) 中我们用于创建泛型函数相反的步骤:编译器查看泛型代码被调用的所有位置,并针对泛型代码被调用时的具体类型生成代码。
|
||||
|
||||
咱们或许想知道,在运用了泛型参数时,是否有着运行时的开销。好消息就是,相比于使用具体类型,使用泛型不会令到咱们的程序运行得更慢。
|
||||
> **译注**
|
||||
>
|
||||
> 参考:[Wikipedia: Monomorphization](https://en.wikipedia.org/wiki/Monomorphization)
|
||||
|
||||
Rust 通过在编译时,完成那些使用了泛型代码的单态化,performing monomorphization of the code using generics,达成这个目的。所谓 *单态化,monomorphization*,是指通过把编译后用到的具体类型,填入到泛型位置,而将通用代码转换为具体代码的过程。在此过程中,编译器会执行与清单 10-5 中,咱们用来创建通用函数相反的步骤:编译器会查看泛型代码被调用到的所有地方,并为那些调用到的泛型代码,生成具体类型代码。
|
||||
|
||||
咱们来通过使用标准库的通用 `Option<T>` 枚举,看看单态化的工作原理:
|
||||
咱们来通过使用标准库的通用 `Option<T>` 枚举看看其工作原理:
|
||||
|
||||
```rust
|
||||
let integer = Some(5);
|
||||
let float = Some(5.0);
|
||||
```
|
||||
|
||||
在编译此代码时,Rust 就会执行单态化。在那过程中,编译器会读取这两个 `Option<T>` 实例中用到的值,并识别到两种类型的 `Option<T>`:一个为 `i32`,而另一个为 `f64`。这样一来,编译器就会把 `Option<T>` 的通用定义,展开为两个专门的 `i32` 与 `f64` 定义,由此就用这些特定类型,替换了通用定义。
|
||||
当 Rust 编译这段代码时,他会执行单态化。在此过程中,编译器读取 `Option<T>` 实例中使用的值,并识别出两种 `Option<T>`:一种是 `i32`,另一种是 `f64`。因此,他展开 `Option<T>` 的泛型定义为特定于 `i32` 和 `f64` 的两个定义,从而以具体定义替换泛型定义。
|
||||
|
||||
单态化的代码版本,看起来与下面的类似(编译器会使用不同于这里为演示目的而使用的名字):
|
||||
这段代码的单态化版本类似于以下内容(编译器会使用与我们在这里用于演示的不同名字):
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -358,12 +358,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
那个通用的 `Option<T>`,就被以编译器创建的具体定义给替换掉了。由于 Rust 会把通用代码,编译到指明了各个实例中类型的代码,因此咱们就不会为运用泛型而付出运行时代价。在代码运行时,其会如同原本咱们曾重复了那些定义的代码一样执行。单态化的过程,令到 Rust 的泛型在运行时极为高效。
|
||||
|
||||
|
||||
|
||||
泛型 `Option<T>` 被编译器创建的具体定义替换。由于 Rust 会将泛型代码编译为指定每个实例中的类型的代码,因此咱们不会付出使用泛型的运行时开销。当代码运行时,他只会如同我们手动重复了每个定义时原本那样执行。单态化的过程使 Rust 的泛型在运行时极为高效。
|
||||
|
||||
|
||||
(End)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user