相信很多C++程序员对泛型编程是非常熟悉的,我本来也是觉得不需要再特别介绍泛型这部分,因为其实都大同小异。但是考虑到部分读者可能不太熟悉,所以还是专门用一期来介绍泛型。
C/C++、Rust都是强类型语言,在对数据进行处理时,必须明确数据的数据类型。但是很多时候,比如链表这种数据结构,我们可以是整型数据的链表,也可以是其他类型,我们可能就会写出重复的代码,仅仅是数据类型不同而已。还有比如说排序,算法里处理数据,也是同样的道理。
所以,我们为了简化代码。我们将类型抽象成一种“参数”,数据和算法针对于这种抽象的类型来实现,而不是具体的类型。当我们需要使用时再去具体化、实例化。
下面举一个例子:
//不使用泛型
//针对于整型数据
fn findmax_int(list : &[i32]) -> i32 {
let mut max_int = list[0];
for &i in list.iter() {
if i > max_int {
max_int = i;
}
}
max_int
}
//针对于char数据
fn findmax_char(list : &[char]) -> char {
let mut max_char = list[0];
for &i in list.iter() {
if i > max_char {
max_char = i;
}
}
max_char
}
fn main() {
let v_int = vec![2, 4, 1, 5, 7, 3];
println!("max_int: {}", findmax_int(&v_int));
let v_char = vec!['A', 'C', 'G', 'B', 'F'];
println!("max_char: {}", findmax_char(&v_char));
}
运行结果:
max_int: 7
max_char: G
可以看到两个函数基本上是一样的,下面我们采用泛型的方式来简化代码:
fn find_max (list : &[T]) -> T {
let mut max = list[0];
for &i in list.iter() {
if i > max {
max = i;
}
}
max
}
fn main() {
let v_int = vec![2, 4, 1, 5, 7, 3];
println!("max_int: {}", find_max(&v_int));
let v_char = vec!['A', 'C', 'G', 'B', 'F'];
println!("max_char: {}", find_max(&v_char));
}
编译报错:
error[E0369]: binary operation `>` cannot be applied to type `T`
--> src\main.rs:27:14
|
27 | if i > max {
| - ^ --- T
| |
| T
|
= note: `T` might need a bound for `std::cmp::PartialOrd`
意思就是>这个运算符需要PartialOrd,这个的意思是你用比较符号,得能比较的数据类型。
那我们加上:
fn find_max (list : &[T]) -> T {
let mut max = list[0];
for &i in list.iter() {
if i > max {
max = i;
}
}
max
}
还是编译报错:
error[E0508]: cannot move out of type `[T]`, a non-copy slice
--> src\main.rs:25:19
|
25 | let mut max = list[0];
| ^^^^^^^
| |
| cannot move out of here
| move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
| help: consider borrowing here: `&list[0]`
error[E0507]: cannot move out of a shared reference
--> src\main.rs:26:15
|
26 | for &i in list.iter() {
| -- ^^^^^^^^^^^
| ||
| |data moved here
| |move occurs because `i` has type `T`, which does not implement the `Copy` trait
| help: consider removing the `&`: `i`
就是说这两个操作需要类型具有Copy语义。因此加上对类型的Copy语义要求:
fn find_max (list : &[T]) -> T {
let mut max = list[0];
for &i in list.iter() {
if i > max {
max = i;
}
}
max
}
fn main() {
let v_int = vec![2, 4, 1, 5, 7, 3];
println!("max_int: {}", find_max(&v_int));
let v_char = vec!['A', 'C', 'G', 'B', 'F'];
println!("max_char: {}", find_max(&v_char));
}
成功运行:
max_int: 7
max_char: G
非常滴方便!
其实泛型的话,还是比较复杂的一个概念,我也是听侯捷老师的课的时候才感觉醍醐灌顶,感兴趣的人可以去看一下侯捷老师关于C++ STL的课程,里面有讲到很多泛型编程的细节,包括泛化、偏特化等等。如果找不到资源可以后台留言或者加我微信哈。
下面开始介绍Rust中的泛型。
例如结构体、枚举吧:
Option就是一个泛型嘛。
enum Option {
Some(T),
None,
}
结构体:
struct A {
data1 : T,
data2 : T,
data3 : i32
}
但是声明了泛型就必须使用:
struct A {
data : i32
}
这样就会报错。
在结构体方法中泛型:
struct A {
x : T,
y : T
}
impl A {
fn get_x(&self) -> &T {
&self.x
}
fn get_y(&self) -> &T {
&self.y
}
}
fn main() {
let a = A { x : 1, y : 2};
println!("a.x: {}, a.y: {}",a.get_x(), a.get_y());
let b = A { x : 'a', y : 's'};
println!("b.x: {}, b.y: {}",b.get_x(), b.get_y());
}
运行结果:
a.x: 1, a.y: 2
b.x: a, b.y: s
这个程序很好理解。下面再举一个例子。
struct A {
x : T,
y : S
}
impl A {
//通过两个不同的A来创建一个新的A
fn crete_newA(self, other : A) -> A {
A {
x : self.x,
y : other.y
}
}
}
fn main() {
let a1 = A { x : 's', y : 2.2 };
let a2 = A { x : 3.5, y : 5 };
let a3 = a1.crete_newA(a2);
println!("a3.x: {}, a3.y: {}", a3.x, a3.y);
}
运行结果:
a3.x: s, a3.y: 5
我们最开始举的例子就是咯。
第一部分举的例子中的PartialOrd + Copy就是泛型约束。
那么什么是泛型约束呢?
Rust中的泛型和C++中的template是比较相似的,但是差别很大。C++是在实例化的时候进行类型检查。而Rust则是当场进行检查,所以需要用户提供合理的“泛型约束”,比如你需要使用“>”时,需要数据类型能够使用“>”运算符。
既然是简单介绍,这期就到这里吧,毕竟在以后的学习中,泛型无处不在。