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执行了泛型代码的单态化:
在编译期间将泛型代码转换成所有可能的特定代码的过程。
trait(特征)被用来向rust编译器描述某些特定类型拥有的且能够被其他类型共享的功能,它使我们可以以一种抽象的方式来定义共享行为。我们还可以使用trait约束来将泛型参数指定为实现了某些特定行为的类型。 定 ...