引言:什么是泛型,为什么 Rust 需要它们?
\n想象一下,你需要编写一个函数来查找列表中的最大元素。对于整数,你需要一个函数;对于浮点数,你需要另一个函数;对于字符串,你还需要第三个函数。代码会重复,维护起来也会变得困难。在 Rust 中,泛型(泛型编程)解决了这个问题。
\n泛型允许你编写能够处理不同数据类型的代码,同时不丢失严格的类型检查。你不必为每个类型重复编写函数,而是编写一个通用版本。Rust 在编译时会替换具体的类型并检查正确性。这带来了以下好处:
\n- \n
- 代码复用 —— 一个函数适用于所有类型。 \n
- 安全性 —— 所有类型错误都在编译时被捕获。 \n
- 性能 —— 得益于单态化(编译器为每个使用的类型生成单独的代码),泛型代码的运行速度与专用代码一样快。 \n
在本文中,我们将通过实际示例,从简单的函数到结构体和 trait,讲解如何在 Rust 中使用泛型。
\n\n1. 函数中的泛型:迈向通用性的第一步
\n语法和基本示例
\n要使函数成为泛型,你需要在尖括号 <T> 中指定一个类型参数。通常使用大写字母:T、U、V。下面是一个返回传入值的函数:
fn identity<T>(value: T) -> T {\n value\n}\n\nfn main() {\n let num = identity(42);\n let text = identity("Hello");\n println!("{} {}", num, text); // 输出:42 Hello\n}\n编译器会根据传入的参数自动推断类型。你也可以显式指定类型:identity::<i32>(42)。
约束:作为类型边界的 trait
\n如果我们想比较值呢?直接写 value1 > value2 是不行的——并非所有类型都支持比较。你需要指定该类型必须实现 PartialOrd trait:
fn largest<T: PartialOrd>(list: &[T]) -> &T {\n let mut largest = &list[0];\n for item in list {\n if item > largest {\n largest = item;\n }\n }\n largest\n}\n\nfn main() {\n let numbers = vec![34, 50, 25, 100, 65];\n println!("最大数字:{}", largest(&numbers));\n\n let chars = vec!['y', 'm', 'a', 'q'];\n println!("最大字符:{}", largest(&chars));\n}\n这里的 T: PartialOrd 是一个 trait 约束。它保证了类型 T 定义了比较运算符。如果没有这个约束,代码将无法编译。
2. 结构体和枚举中的泛型
\n具有泛型字段的结构体
\n泛型也适用于自定义类型。例如,Point 结构体可以存储任何类型的坐标:
struct Point<T> {\n x: T,\n y: T,\n}\n\nfn main() {\n let integer_point = Point { x: 5, y: 10 };\n let float_point = Point { x: 1.0, y: 4.0 };\n // let mixed = Point { x: 5, y: 4.0 }; // 错误!两个字段必须是同一类型\n}\n如果你希望字段可以是不同的类型,请使用多个参数:
\nstruct Point<T, U> {\n x: T,\n y: U,\n}\n\nfn main() {\n let mixed = Point { x: 5, y: 4.0 }; // 现在可以工作了\n}\n\n具有泛型的枚举
\n标准枚举 Option<T> 是泛型枚举的一个很好的例子。你也可以创建自己的枚举:
enum Result<T, E> {\n Ok(T),\n Err(E),\n}\n\n// 使用\nfn divide(a: f64, b: f64) -> Result<f64, String> {\n if b