在 Rust 中编写泛型代码
学习编写适用于多种类型的函数和数据结构,在不牺牲类型安全的情况下提升代码复用性。
在 Rust 中编写泛型代码 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 1 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。
为什么要编写泛型代码?
假设您需要一个函数来找出列表中最大的项。如果您需要它处理数字、字符,之后还要处理自定义对象,该怎么办?
没有泛型,您需要为每种类型分别编写一个函数,这会产生大量重复代码。这正是泛型发挥作用的地方!
介绍泛型
泛型让您能够编写适用于多种类型的代码,而不必重复编写相同内容。它是一种编写灵活且可复用的函数或数据结构的方式。
您可以把它想象成一个能够适应不同材料的蓝图。
您的第一个泛型函数
要让函数成为泛型函数,请在函数名称后面的尖括号 <> 中声明类型参数。常见的类型参数名称是 T(表示 Type,即类型)。
现在,这个 print_anything 函数可以打印任何类型的值了!
fn print_anything<T>(item: T) {
println!("The item is: {}", item);
}
pub fn main() {
print_anything(5);
print_anything("hello");
print_anything(true);
}解释类型参数
fn print_anything<T>(item: T) 中的 <T> 表示 T 是类型的占位符。当您使用 i32 调用该函数时,T 就会变为 i32。
- 类型参数:泛型类型通常使用大写字母命名,例如
T、U、V。 - 灵活性:编译器会在编译时推断出具体类型。
添加行为:trait bound
有时,您的泛型函数要求其类型参数 T 具备特定行为。例如,如果您想比较两个 T,那么 T 必须是可比较的。
我们可以添加 trait bound 来指定这些要求。这里,T: PartialOrd + Copy 表示 T 必须实现 PartialOrd(用于比较的部分排序)和 Copy trait。
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
pub fn main() {
let number_list = vec![34, 50, 25, 100, 65];
println!("Largest number: {}", largest(&number_list));
let char_list = vec!['y', 'm', 'a', 'q'];
println!("Largest char: {}", largest(&char_list));
}多个 trait bound 的语法
您可以使用 + 语法为泛型类型要求多个 trait,例如 T: TraitA + TraitB。
对于复杂的 bound,还可以在函数签名后使用 where 子句,这样可以让签名更简洁:
fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug { /* ... */ }
泛型结构体
与函数一样,您也可以让结构体接受一个或多个类型参数作为泛型。这样,数据结构就能存储任意指定类型的数据。
Point<T> 结构体可以存储任意类型 T 的坐标(例如 i32、f64)。
struct Point<T> {
x: T,
y: T,
}
pub fn main() {
let integer_point = Point {
x: 5,
y: 10
};
let float_point = Point {
x: 1.0,
y: 4.0
};
println!("Int Point: ({}, {})",
integer_point.x, integer_point.y);
println!("Float Point: ({}, {})",
float_point.x, float_point.y);
}为泛型结构体实现方法
为泛型结构体实现方法时,需要在 impl 后指定泛型类型参数。
如果某个方法特别需要泛型类型具备某种行为,您还可以为该方法添加 trait bound。
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
pub fn main() {
let p = Point {
x: 5,
y: 10
};
println!("p.x = {}", p.x());
}泛型与性能
Rust 的优势之一是泛型属于零成本抽象。这意味着使用泛型不会带来运行时性能损失。
Rust 通过单态化实现这一点:在编译时,编译器会针对泛型代码所使用的每种具体类型生成专门的版本。因此,largest<i32> 和 largest<char> 会变成两个不同且经过优化的函数。
检验您的知识
以下关于 Rust 泛型的说法,哪些是正确的?
回顾:泛型的力量
在本课中,您学习了在 Rust 中编写泛型代码的基础知识:
- 泛型是什么:一种编写灵活、可复用代码的方式。
- 泛型函数:使用
<T>表示类型参数。 - trait bound:使用
T: Trait指定所需行为。 - 泛型结构体:创建能够存储泛型类型的数据结构。
- 零成本:Rust 会将泛型编译为具体代码,确保不会产生运行时损失。
泛型是符合 Rust 惯用风格的代码的基石,能够实现强大且类型安全的抽象!
常见问题解答
「在 Rust 中编写泛型代码」课时是免费的吗?
是的 — 「在 Rust 中编写泛型代码」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。
「在 Rust 中编写泛型代码」这节课中我会学到什么?
学习编写适用于多种类型的函数和数据结构,在不牺牲类型安全的情况下提升代码复用性。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 3 节。
「在 Rust 中编写泛型代码」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 在 Rust 中编写泛型代码
- 定义和实现 trait
- 高级 trait 用法:关联类型