Updated src/generic_types_traits_and_lifetimes/generics.md'.

This commit is contained in:
Hector PENG
2026-03-22 14:55:09 +08:00
parent ba0e76f3dc
commit 544f0ce80d
2 changed files with 35 additions and 38 deletions

View File

@@ -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);
}

View File

@@ -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