Rust 泛型数据类型
泛型运行代码作用于抽象的类型
在函数定义中使用泛型
当使用泛型来定义函数时,将泛型放在函数签名中指定参数和返回值类型的地方。
首先我们来看两个函数:
fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> char {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest_i32(&number_list);
println!("The largest number is {}", result);
assert_eq!(result, 100);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest_char(&char_list);
println!("The largest char is {}", result);
assert_eq!(result, 'y');
}
largest_i32和largest_char其实实现了相同的功能,只是适用于不同的数据类型。我们可以通过泛型来消除重复代码。
fn largest<T>(list: &[T]) -> T {
我们首先需要给泛型命名,你可以适用任何合法标识符来命名泛型,习惯上我们使用T,在函数名后加尖括号T,并在参数和返回值需要的地方,加上泛型。 以上的代码可以理解为:函数largest拥有泛型参数T,它接收一个名为list的T值切片作为参数,并返回一个同样拥有类型T的值作为结果。
接下来,我们使用修改后的泛型代码,但是代码还是错误的,我们慢慢来修复它:
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
报错信息如下:
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0369]: binary operation `>` cannot be applied to type `T`
--> src/main.rs:5:17
|
5 | if item > largest {
| ---- ^ ------- T
| |
| T
|
= note: `T` might need a bound for `std::cmp::PartialOrd`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0369`.
error: could not compile `chapter10`.
To learn more, run the command again with --verbose.
说的是std::cmp::PartialOrd是一个trait,我们将在下一节来讨论它。简单的说就是,并不是所有类型都是可排序的。我们可以通过实现std::cmp::PartialOrd这个trait来为类型实现比较功能。如何使用trait之后会讨论,我们先看看其他可能用到泛型参数的地方。
在结构体定义中使用泛型
依旧时在名称后加一个尖括号T。
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
}
注意,尖括号中只定义了一种泛型T,所以x和y无论类型是什么,他们的类型都是相同的。下面这种代码是无法通过编译的:
struct Point<T> {
x: T,
y: T,
}
fn main() {
let wont_work = Point { x: 5, y: 4.0 };
}
因为这里的x和y不是同一种类型。
如果需要将x和y实例化为不同类型,那么就需要声明两个泛型:
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}
在枚举定义中使用泛型
典范:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
在方法定义中使用泛型
方法定义泛型有些特殊,是要在impl后就定义泛型,point< T >。通过在impl之后将T声明为泛型,Rust能够识别出Point尖括号内的类型是泛型而不是具体类型。
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
泛型代码的性能问题
rust实现的泛型于具体类型的代码相比,在速度上没有差异。 因为rust执行了泛型代码的单态化: 在编译期间将泛型代码转换成所有可能的特定代码的过程。
下一章:Rust Trait 的使用
trait(特征)被用来向rust编译器描述某些特定类型拥有的且能够被其他类型共享的功能,它使我们可以以一种抽象的方式来定义共享行为。我们还可以使用trait约束来将泛型参数指定为实现了某些特定行为的类型。 定 ...
AI 中文社