x-i18n: generated_at: “2026-03-01T14:04:40Z” model: gemini-3-flash-preview provider: google-gemini-cli source_hash: d020e896ebc0b986fcddcbab0511462ebb9e4ca4b6ebdb456b2993041f2614af source_path: ch10-00-generics.md workflow: 16
泛型、特征与生命周期 (Generic Types, Traits, and Lifetimes)
Generic Types, Traits, and Lifetimes
每种编程语言都有能有效处理重复概念的工具。在 Rust 中,这种工具之一就是“泛型 (generics)”:它是具体类型或其他属性的抽象替身。我们可以表达泛型的行为或它们与其他泛型之间的关系,而无需在编译和运行代码时知道它们的具体内容。
Every programming language has tools for effectively handling the duplication of concepts. In Rust, one such tool is generics: abstract stand-ins for concrete types or other properties. We can express the behavior of generics or how they relate to other generics without knowing what will be in their place when compiling and running the code.
函数可以接收某种泛型类型的参数,而不是像 i32 或 String 这样的具体类型,这就像它们接收具有未知值的参数以便在多个具体值上运行相同的代码一样。事实上,我们已经在第 6 章的 Option<T>、第 8 章的 Vec<T> 和 HashMap<K, V>,以及第 9 章的 Result<T, E> 中使用过泛型了。在本章中,你将探索如何使用泛型定义自己的类型、函数和方法!
Functions can take parameters of some generic type, instead of a concrete type
like i32 or String, in the same way they take parameters with unknown
values to run the same code on multiple concrete values. In fact, we already
used generics in Chapter 6 with Option<T>, in Chapter 8 with Vec<T> and
HashMap<K, V>, and in Chapter 9 with Result<T, E>. In this chapter, you’ll
explore how to define your own types, functions, and methods with generics!
首先,我们将回顾如何通过提取函数来减少代码重复。然后,我们将使用相同的技术,从两个仅在参数类型上有所不同的函数中创建一个泛型函数。我们还将解释如何在结构体和枚举定义中使用泛型类型。
First, we’ll review how to extract a function to reduce code duplication. We’ll then use the same technique to make a generic function from two functions that differ only in the types of their parameters. We’ll also explain how to use generic types in struct and enum definitions.
接着,你将学习如何使用“特征 (traits)”以泛型的方式定义行为。你可以将特征与泛型类型结合使用,以约束泛型类型仅接受那些具有特定行为的类型,而不仅仅是任何类型。
Then, you’ll learn how to use traits to define behavior in a generic way. You can combine traits with generic types to constrain a generic type to accept only those types that have a particular behavior, as opposed to just any type.
最后,我们将讨论“生命周期 (lifetimes)”:这是一种特殊的泛型,它为编译器提供有关引用如何相互关联的信息。生命周期允许我们向编译器提供有关借用值的足够信息,以便它能在更多的情况下确保引用是有效的,而这在没有我们帮助的情况下它是做不到的。
Finally, we’ll discuss lifetimes: a variety of generics that give the compiler information about how references relate to each other. Lifetimes allow us to give the compiler enough information about borrowed values so that it can ensure that references will be valid in more situations than it could without our help.
通过提取函数消除重复 (Removing Duplication by Extracting a Function)
Removing Duplication by Extracting a Function
泛型允许我们用一个代表多种类型的占位符来替换特定的类型,从而消除代码重复。在深入研究泛型语法之前,让我们先看看在不涉及泛型类型的情况下,如何通过提取一个函数来消除重复,该函数用一个代表多个值的占位符替换特定的值。然后,我们将应用相同的技术来提取一个泛型函数!通过观察如何识别可以提取到函数中的重复代码,你将开始识别可以使用泛型的重复代码。
Generics allow us to replace specific types with a placeholder that represents multiple types to remove code duplication. Before diving into generics syntax, let’s first look at how to remove duplication in a way that doesn’t involve generic types by extracting a function that replaces specific values with a placeholder that represents multiple values. Then, we’ll apply the same technique to extract a generic function! By looking at how to recognize duplicated code you can extract into a function, you’ll start to recognize duplicated code that can use generics.
我们将从示例 10-1 中的简短程序开始,该程序寻找列表中的最大数字。
We’ll begin with the short program in Listing 10-1 that finds the largest number in a list.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-01/src/main.rs:here}}
}
我们在变量 number_list 中存储一个整数列表,并将该列表中第一个数字的引用放在名为 largest 的变量中。然后我们遍历列表中的所有数字,如果当前数字大于存储在 largest 中的数字,我们就替换该变量中的引用。然而,如果当前数字小于或等于目前看到的最大数字,变量就不会改变,代码继续处理列表中的下一个数字。在考虑了列表中的所有数字后,largest 应该引用最大的数字,在此例中为 100。
We store a list of integers in the variable number_list and place a reference
to the first number in the list in a variable named largest. We then iterate
through all the numbers in the list, and if the current number is greater than
the number stored in largest, we replace the reference in that variable.
However, if the current number is less than or equal to the largest number seen
so far, the variable doesn’t change, and the code moves on to the next number
in the list. After considering all the numbers in the list, largest should
refer to the largest number, which in this case is 100.
现在我们的任务是寻找两个不同数字列表中的最大数字。为此,我们可以选择复制示例 10-1 中的代码,并在程序的两个不同地方使用相同的逻辑,如示例 10-2 所示。
We’ve now been tasked with finding the largest number in two different lists of numbers. To do so, we can choose to duplicate the code in Listing 10-1 and use the same logic at two different places in the program, as shown in Listing 10-2.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}}
}
虽然这段代码可以工作,但复制代码是乏味且容易出错的。当我们想要更改代码时,我们还必须记得在多个地方更新它。
Although this code works, duplicating code is tedious and error-prone. We also have to remember to update the code in multiple places when we want to change it.
为了消除这种重复,我们将通过定义一个操作任何作为参数传入的整数列表的函数来创建一个抽象。这个解决方案使我们的代码更清晰,并让我们能抽象地表达在列表中寻找最大数字的概念。
To eliminate this duplication, we’ll create an abstraction by defining a function that operates on any list of integers passed in as a parameter. This solution makes our code clearer and lets us express the concept of finding the largest number in a list abstractly.
在示例 10-3 中,我们将寻找最大数字的代码提取到一个名为 largest 的函数中。然后,我们调用该函数来寻找示例 10-2 中两个列表中的最大数字。我们也可以在将来可能拥有的任何其他 i32 值列表上使用该函数。
In Listing 10-3, we extract the code that finds the largest number into a
function named largest. Then, we call the function to find the largest number
in the two lists from Listing 10-2. We could also use the function on any other
list of i32 values we might have in the future.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-03/src/main.rs:here}}
}
largest 函数有一个名为 list 的参数,它代表我们可能传递给函数的任何具体的 i32 值切片。因此,当我们调用该函数时,代码会在我们传入的具体值上运行。
The largest function has a parameter called list, which represents any
concrete slice of i32 values we might pass into the function. As a result,
when we call the function, the code runs on the specific values that we pass
in.
总而言之,以下是我们将代码从示例 10-2 更改为示例 10-3 所采取的步骤:
In summary, here are the steps we took to change the code from Listing 10-2 to Listing 10-3:
-
识别重复代码。
-
将重复代码提取到函数体中,并在函数签名中指定该代码的输入和返回值。
-
更新重复代码的两个实例以改为调用该函数。
-
Identify duplicate code.
-
Extract the duplicate code into the body of the function, and specify the inputs and return values of that code in the function signature.
-
Update the two instances of duplicated code to call the function instead.
接下来,我们将使用同样的步骤配合泛型来减少代码重复。就像函数体可以在抽象的 list 上而不是具体的值上操作一样,泛型允许代码在抽象类型上操作。
Next, we’ll use these same steps with generics to reduce code duplication. In
the same way that the function body can operate on an abstract list instead
of specific values, generics allow code to operate on abstract types.
例如,假设我们有两个函数:一个寻找 i32 值切片中的最大项,另一个寻找 char 值切片中的最大项。我们该如何消除这种重复呢?让我们一探究竟!
For example, say we had two functions: one that finds the largest item in a
slice of i32 values and one that finds the largest item in a slice of char
values. How would we eliminate that duplication? Let’s find out!