rust 泛型
本章内容,初学只需要掌握基本泛型语法和泛型函数即可,示例代码和泛型结构体以及泛型trait等对应章节学完再学。
1. 泛型编程是什么?目的是什么?
泛型编程是一种编程范式,让你在编写代码时不指定具体的数据类型,而是使用“类型占位符”。当你实际使用这段代码时,再填入具体的类型(如 i32、f64、String 等)。
1.1 为什么需要泛型?
假设你要编写一个函数,用来交换两个整数的值:
#![allow(unused)]
fn main() {
fn swap_i32(a: i32, b: i32) -> (i32, i32) {
(b, a)
}
}
如果还要交换两个浮点数,你可能再写一个函数:
#![allow(unused)]
fn main() {
fn swap_f64(a: f64, b: f64) -> (f64, f64) {
(b, a)
}
}
这样会带来大量重复代码,而且每个新类型都需要重新实现。泛型正是为了解决这类问题而生的——你只需写一个泛型函数,就可以处理所有类型:
#![allow(unused)]
fn main() {
fn swap<T>(a: T, b: T) -> (T, T) {
(b, a)
}
}
其中 <T> 是一个类型占位符,调用时 Rust 会根据实际传入的参数自动推断出具体的 T(比如 i32、f64 或 String)。一份代码,任意类型,完美复用。
1.2 泛型的目的
- 代码复用:一份代码可以服务于多种类型。
- 类型安全:编译时仍然会进行严格的类型检查,避免运行时类型错误。
- 提高抽象能力:让你专注于算法和结构,而不是具体类型。
2. 泛型的基本语法
Rust 中用尖括号 <T> 声明泛型参数,T 是习惯使用的名称(可以任意命名,但通常用大写字母)。下面分别介绍泛型在函数、结构体以及 trait 中的用法。
2.1 泛型函数
基本语法结构
fn 函数名<类型参数>(参数名: 类型参数) -> 返回类型 {
// 函数体
}
调用示例片段(仅展示定义及调用方式)
#![allow(unused)]
fn main() {
fn identity<T>(value: T) -> T {
value
}
// 调用示例(类型自动推断)
let a = identity(42); // T 为 i32
let b = identity(3.14); // T 为 f64
}
2.2 泛型结构体
基本语法结构
struct 结构体名<类型参数> {
字段名: 类型参数,
// 其它字段...
}
调用示例片段
#![allow(unused)]
fn main() {
struct Box<T> {
item: T,
}
impl<T> Box<T> {
fn new(item: T) -> Self {
Box { item }
}
fn get(&self) -> &T {
&self.item
}
}
// 创建不同类型的实例
let int_box = Box::new(100); // Box<i32>
let str_box = Box::new(String::from("hello")); // Box<String>
}
2.3 泛型 trait
基本语法结构
trait Trait名<类型参数> {
// 方法签名中可以使用该类型参数
fn 方法名(&self, 参数: 类型参数);
}
与结构体不同,trait 的泛型参数表示该 trait 是“泛型化的”,实现时可以为同一个类型多次实现该 trait(针对不同的具体类型)。
调用示例片段
#![allow(unused)]
fn main() {
trait ToString<T> {
fn convert(&self, value: T) -> String;
}
// 为 i32 实现 ToString<f64>
impl ToString<f64> for i32 {
fn convert(&self, value: f64) -> String {
format!("整数 {} 和 浮点数 {}", self, value)
}
}
// 调用
let num = 42;
let result = num.convert(3.14); // result 为 String 类型
}
3. 综合示例:一个泛型容器(带讲解)
下面我们实现一个更完整的例子:一个可以存储两个相同类型值的对偶容器 Pair,并演示其在不同类型下的完整使用。
// 泛型结构体:存储两个相同类型的值
struct Pair<T> {
first: T,
second: T,
}
impl<T> Pair<T> {
// 构造函数
fn new(first: T, second: T) -> Self {
Pair { first, second }
}
// 获取第一个值的引用
fn first(&self) -> &T {
&self.first
}
// 获取第二个值的引用
fn second(&self) -> &T {
&self.second
}
// 交换两个值的位置
fn swap(&mut self) {
std::mem::swap(&mut self.first, &mut self.second);
}
// 消费 self,返回两个值的元组
fn into_tuple(self) -> (T, T) {
(self.first, self.second)
}
}
// 一个泛型函数:取出 Pair 的第一个字段
fn take_first<T>(pair: Pair<T>) -> T {
pair.first
}
fn main() {
// 使用整数类型
let mut int_pair = Pair::new(10, 20);
println!("({}, {})", int_pair.first(), int_pair.second());
int_pair.swap();
println!("交换后: ({}, {})", int_pair.first(), int_pair.second());
// 使用字符串类型
let str_pair = Pair::new(String::from("Hello"), String::from("World"));
let (s1, s2) = str_pair.into_tuple();
println!("字符串对偶: ({}, {})", s1, s2);
// 使用浮点类型 + 泛型函数
let float_pair = Pair { first: 3.14, second: 2.718 };
let first_val = take_first(float_pair);
println!("取出的第一个浮点数: {}", first_val);
}