泛型、trait 与生命周期

优质
小牛编辑
130浏览
2023-12-01

每一个编程语言都有高效的处理重复概念的工具;在 Rust 中其工具之一就是 泛型generics)。泛型是具体类型或其他属性的抽象替代。我们可以表达泛型的属性,比如他们的行为或如何与其他泛型相关联,而不需要在编写和编译代码时知道他们在这里实际上代表什么。

同理为了编写一份可以用于多种具体值的代码,函数并不知道其参数为何值,这时就可以让函数获取泛型而不是像 或 String 这样的具体值。我们已经使用过第六章的 Option<T>,第八章的 Vec<T>HashMap<K, V>,以及第九章的 Result<T, E> 这些泛型了。本章会探索如何使用泛型定义我们自己的类型、函数和方法!

首先,我们将回顾一下提取函数以减少代码重复的机制。接着使用一个只在参数类型上不同的泛型函数来实现相同的功能。我们也会讲到结构体和枚举定义中的泛型。

之后,我们讨论 trait,这是一个定义泛型行为的方法。trait 可以与泛型结合来将泛型限制为拥有特定行为的类型,而不是任意类型。

最后介绍 生命周期lifetimes),它是一类允许我们向编译器提供引用如何相互关联的泛型。Rust 的生命周期功能允许在很多场景下借用值的同时仍然使编译器能够检查这些引用的有效性。

在介绍泛型语法之前,首先来回顾一个不使用泛型的处理重复的技术:提取一个函数。当熟悉了这个技术以后,我们将使用相同的机制来提取一个泛型函数!如同你识别出可以提取到函数中重复代码那样,你也会开始识别出能够使用泛型的重复代码。

文件名: src/main.rs

示例 10-1:在一个数字列表中寻找最大值的函数

这段代码获取一个整型列表,存放在变量 中。它将列表的第一项放入了变量 largest 中。接着遍历了列表中的所有数字,如果当前值大于 largest 中储存的值,将 largest 替换为这个值。如果当前值小于目前为止的最大值,largest 保持不变。当列表中所有值都被考虑到之后,largest 将会是最大值,在这里也就是 100。

如果需要在两个不同的列表中寻找最大值,我们可以重复示例 10-1 中的代码,这样程序中就会存在两段相同逻辑的代码,如示例 10-2 所示:

Filename: src/main.rs

示例 10-2:寻找 两个 数字列表最大值的代码

为了消除重复,我们可以创建一层抽象,在这个例子中将表现为一个获取任意整型列表作为参数并对其进行处理的函数。这将增加代码的简洁性并让我们将表达和推导寻找列表中最大值的这个概念与使用这个概念的特定位置相互独立。

在示例 10-3 的程序中将寻找最大值的代码提取到了一个叫做 的函数中。这个程序可以找出两个不同数字列表的最大值,不过示例 10-1 中的代码只存在于一个位置:

文件名: src/main.rs

示例 10-3:抽象后的寻找两个数字列表最大值的代码

这个函数有一个参数 list,它代表会传递给函数的任何具体的 i32值的 slice。函数定义中的 list 代表任何 &[i32]。当调用 largest 函数时,其代码实际上运行于我们传递的特定值上。

从示例 10-2 到示例 10-3 中涉及的机制经历了如下几步:

    如果我们有两个函数,一个寻找一个 值的 slice 中的最大项而另一个寻找 char 值的 slice 中的最大项该怎么办?该如何消除重复呢?让我们拭目以待!