泛型类型、特征和生命周期

每种编程语言都有有效处理重复的工具 的概念。在 Rust 中,一个这样的工具是 generics:抽象的替代项 具体类型或其他属性。我们可以表示泛型 或 它们如何与其他泛型相关联,而不知道它们的位置上会有什么 在编译和运行代码时。

函数可以采用某种泛型类型的参数,而不是具体类型 like 或 ,它们采用带有 unknown 的参数 values 对多个具体值运行相同的代码。事实上,我们已经 在第 6 章中使用了泛型,在第 8 章中使用了 和 ,在第 9 章中使用了泛型。在本章中,您将 探索如何使用泛型定义您自己的类型、函数和方法!i32StringOption<T>Vec<T>HashMap<K, V>Result<T, E>

首先,我们将回顾如何提取函数以减少代码重复。我们将 然后使用相同的技术从两个函数中创建一个泛型函数,这两个函数 仅在参数类型上有所不同。我们还将解释如何使用 struct 和 enum 定义中的泛型类型。

然后,您将学习如何使用 trait 以通用方式定义行为。你 可以将 trait 与泛型类型组合在一起,以约束泛型类型接受 仅那些具有特定行为的类型,而不是任何类型。

最后,我们将讨论生命周期:各种泛型,它们为 编译器 有关引用如何相互关联的信息。使用寿命允许 us 为编译器提供有关 borrowed values 的足够信息,以便它可以 确保引用在更多情况下有效,而不是没有 帮助。

通过提取函数删除重复项

泛型允许我们将特定类型替换为代表 multiple types 来删除代码重复。在深入研究泛型语法之前, 让我们首先看看如何以不涉及 泛型类型,方法是提取一个函数,该函数将特定值替换为 placeholder 表示多个值。然后我们将应用相同的 提取泛型函数的技术!通过查看如何识别 您可以提取到函数中的重复代码,您将开始识别 可以使用泛型的重复代码。

我们将从示例 10-1 中的短程序开始,它找到最大的 number 中。

文件名: 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}");
    assert_eq!(*largest, 100);
}

示例 10-1:在 列表中查找最大数字 数字

我们在变量中存储一个整数列表并放置一个引用 添加到名为 .然后我们迭代 遍历列表中的所有数字,并且当前数字大于 存储在 中的数字将替换该变量中的引用。 但是,如果当前数字小于或等于看到的最大数字 到目前为止,变量没有改变,代码会移动到下一个数字 在列表中。在考虑了列表中的所有数字之后,应该 请参阅最大数字,在本例中为 100。number_listlargestlargestlargest

我们现在的任务是在两个不同的 数字。为此,我们可以选择复制示例 10-1 中的代码并使用 相同的 logic 在程序中的两个不同位置,如示例 10-2 所示。

文件名: 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}");

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

示例 10-2:在两个数字列表中查找最大数字的代码

尽管此代码有效,但复制代码既乏味又容易出错。我们还 当我们想要更改时,必须记住在多个地方更新代码 它。

为了消除这种重复,我们将通过定义一个 函数,该函数对作为参数传入的任何整数列表进行作。这 solution 使我们的代码更清晰,并让我们表达查找 abstract list 中的 largest 数字。

在示例 10-3 中,我们将找到最大数字的代码提取到 名为 .然后我们调用函数来找到最大的数字 在示例 10-2 的两个列表中。我们也可以在任何其他 我们将来可能拥有的值列表。largesti32

文件名: src/main.rs

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);
    println!("The largest number is {result}");
    assert_eq!(*result, 100);

    let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];

    let result = largest(&number_list);
    println!("The largest number is {result}");
    assert_eq!(*result, 6000);
}

示例 10-3:用于查找最大数字的抽象代码 分为两个列表

该函数有一个名为 的参数,它表示任何 具体 Slice 的值。因此, 当我们调用函数时,代码将对我们传递的特定值运行 在。largestlisti32

总之,以下是我们将代码从示例 10-2 更改为 示例 10-3:

  1. 识别重复代码。
  2. 将重复的代码提取到函数的主体中,并指定 函数签名中该代码的输入和返回值。
  3. 更新重复代码的两个实例以改为调用该函数。

接下来,我们将对泛型使用这些相同的步骤来减少代码重复。在 与 Function Body 可以对 Abstract 进行作的方式相同 的特定值,泛型允许代码对抽象类型进行作。list

例如,假设我们有两个函数:一个用于查找 Slice 的值,以及一个在 Values 切片中查找最大项的 Slice 值。我们将如何消除这种重复?让我们来了解一下!i32char

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准