IB-Rust-泛型
泛型的例子
用同一功能的函数处理不同类型的数据,例如两个数的加法,无论是整数、浮点数、还是自定义类型,都能支持。在不支持泛型的编程语言中,需要为每种类型编写一个函数:
1 | fn add_i8(a:i8, b:i8) -> i8 { |
泛型
当使用泛型定义函数时,本来在函数签名中指定参数和返回值的类型的地方,会改用泛型来表示。这种技术能使代码适应性更强,从而为函数调用者提供更多的功能,同时也避免了代码重复
1 | fn add<T>(a:T, b:T) -> T { |
上面代码的 T 就是泛型参数,泛型参数的名称可以为任意,但是惯例一般用 T作为首选。该名称越短越好,除非需要表达含义
不是所有 T 类型都能进行相加操作,因此需要用 std::ops::Add<Output = T> 对 T 进行限制:
1 | fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T { |
在函数定义中使用泛型
不是所有的类型都能进行比较,使用标准库中定义的 std::cmp::PartialOrd trait 可以实现类型的比较功能,限制 T 只对实现了 PartialOrd 的类型有效(i32 和 char)
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> &T { |
结构体定义中的泛型
- 结构体名称后面的尖括号中声明泛型参数的名称,结构体定义中可以指定具体数据类型的位置
- x 和 y 是相同的类型
1 | struct Point<T> { |
定义一个 x 和 y 有不同类型且仍是泛型的 Point 结构体:
1 | struct Point<T, U> { |
枚举中使用泛型
1 | enum Option<T> { |
方法中使用泛型
其他 T 不是 i32 类型的 Point<T> 实例没有定义此方法
1 | struct Point<T> { |
impl 之后声明泛型 T
泛型参数可以与结构体定义中声明的泛型参数不同
1 | struct Point<T> { |
方法使用了与结构体定义中不同类型的泛型
1 | struct Point<X1, Y1> { |
const 泛型(Rust 1.51 版本引入的重要特性)
[i32; 3] 和 [i32; 2] 确实是两个完全不同的类型,因此无法用同一个函数调用
1 | fn display_array(arr: [i32; 3]) { |
方式1: 切片+泛型
- 使用数组切片,传入
arr的不可变引用 i32改成所有类型T的数组- 需要对
T加一个限制std::fmt::Debug,表明T可以用在println!("{:?}", arr)中,因为{:?}形式的格式化输出需要arr实现该特征
1 | fn display_array<T: std::fmt::Debug>(arr: &[T]) { |
方式2: const 泛型
const N: usize,表示 const 泛型 N 基于的值类型是 usize
定义一个类型为 [T; N] 的数组:其中的 N 是一个基于值的泛型参数,因为它用来替代数组的长度
1 | fn display_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) { |
泛型代码的性能
Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率
对于标准库中的 Option 枚举:
1 | let integer = Some(5); |
编译器生成的单态化版本的代码看起来像这样(名字为假想的):
1 | enum Option_i32 { |
泛型 Option<T> 被编译器替换为了具体的定义。Rust 会将每种情况下的泛型代码编译为具体类型,使用泛型没有运行时开销。代码运行的执行效率和手写每个具体定义的重复代码一样。单态化正是 Rust 泛型在运行时极其高效的原因
但是,Rust 在编译期为泛型对应的多个类型,生成各自的代码,因此损失了编译速度,且增大了最终生成文件的大小
- Title: IB-Rust-泛型
- Author: Gabrielle
- Created at : 2025-06-05 22:21:27
- Updated at : 2025-06-05 23:36:21
- Link: https://zoella-w.github.io/2025/06/05/75-IB-Rust-泛型/
- License: This work is licensed under CC BY-NC-SA 4.0.