开发环境
- Windows 10
- Rust 1.69.0
- VS Code 1.77.3
项目工程
这里继续沿用上次工程rust-demo
泛型、特性和生命期
每种编程语言都有有效处理概念重复的工具。在Rust中,一个这样的工具就是泛型:具体类型或其他属性的抽象替身。我们可以表达泛型的行为或它们与其他泛型的关系,而不知道在编译和运行代码时它们的位置会是什么。
函数可以接受一些泛型的参数,而不是像i32或String这样的具体类型,就像函数接受未知值的参数一样,对多个具体值运行相同的代码。事实上,我们已经在之前的章节的Option<T>、向量Vec<T>和HashMap<K, V>,以及Result<T, E>中使用了泛型。在这一章中,你将探索如何用泛型定义你自己的类型、函数和方法!
首先,我们将回顾如何提取一个函数以减少代码的重复。然后,我们将使用同样的技术,从两个仅在参数类型上有差异的函数中提取出一个泛型函数。我们还将解释如何在结构和枚举的定义中使用泛型。
然后你将学习如何使用特质以通用方式定义行为。你可以将特质与泛型结合起来,约束泛型只接受那些具有特定行为的类型,而不是任何类型。
最后,我们将讨论生命期:各种泛型,给编译器提供关于引用如何相互关联的信息。生命期允许我们给编译器提供足够的关于借用值的信息,这样它就可以确保引用在更多的情况下是有效的,而不需要我们的帮助。
通过提取一个函数来消除重复
泛型允许我们用一个代表多种类型的占位符来替换特定的类型,以消除代码的重复。在深入研究泛型语法之前,让我们先看看如何通过提取一个用代表多个值的占位符替换特定值的函数,以一种不涉及泛型类型的方式来消除重复。然后,我们将应用同样的技术来提取一个泛型的函数! 通过研究如何识别可以提取到一个函数中的重复代码,你将开始识别可以使用泛型的重复代码。
我们从示例1中的短程序开始,它可以找到一个数组种的最大的数字。
文件名: src/main.rs
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let mut largest = &number_list[0];
for number in &number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
示例1 寻找数组中的最大数字
我们在变量number_list中存储一个数组,并将数组中第一个数字的引用放在一个名为maximum的变量中。然后我们遍历数组中的所有数字,如果当前数字大于存储在largest中的数字,则替换该变量中的引用。然而,如果当前的数字小于或等于到目前为止所看到的最大的数字,变量就不会改变,代码就会转到数组中的下一个数字。在考虑了数组中的所有数字后,largest应该指的是最大的数字,在本例中是100。
我们现在的任务是在两个不同的数组中找出最大的数字。为此,我们可以选择复制清单10-1中的代码,在程序中的两个不同地方使用相同的逻辑,如示例2所示。
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let mut largest = &number_list[0];
for number in &number_list { // 遍历数组
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let mut largest = &number_list[0];
for number in &number_list { // 遍历数组
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
示例2:寻找两个数组中最大数字的代码
虽然这段代码是有效的,但重复代码是繁琐的,而且容易出错。当我们想改变代码时,我们还得记住在多个地方更新代码。
为了消除这种重复,我们将通过定义一个函数来创建一个抽象,该函数可以对参数中传递的任何整数的数组进行操作。这个解决方案使我们的代码更加清晰,并使我们能够抽象地表达寻找数组中最大数字的概念。
在示例3中,我们将寻找最大数字的代码提取到一个名为maximum的函数中。然后,我们调用这个函数来寻找示例2中两个数组中的最大数字。我们也可以在将来可能出现的任何其他i32值的数组中使用这个函数。
fn largest(list: &[i32]) -> &i32 { // 通用函数,用于计算数组种的最大值
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list); // 调用函数largest
println!("The largest number is {}", result);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let result = largest(&number_list); // 调用函数largest
println!("The largest number is {}", result);
}
示例3:查找两个数组中最大数字的抽象代码
最大的函数有一个叫做list的参数,它代表了我们可能传入函数的任何具体的i32切片。因此,当我们调用该函数时,代码在我们传入的具体数值上运行。
综上所述,以下是我们将代码从示例2改为示例3的步骤:
- 识别重复的代码。
- 将重复的代码提取到函数的主体中,并在函数签名中指定该代码的输入和返回值。
- 更新两个重复代码的实例,以调用该函数代替。
接下来,我们将使用这些相同的步骤,用泛型来减少代码的重复。就像函数主体可以对抽象list而不是具体数值进行操作一样,泛型允许代码对抽象类型进行操作。
例如,假设我们有两个函数:一个是在i32值的切片中寻找最大的项目,一个是在char值的切片中寻找最大的项目。我们将如何消除这种重复呢?让我们拭目以待!
本章重点
- 泛型的基本概念
- 函数抽象:提取重复代码
- 求取数组中的最大数方法