在学习了Rust的泛型后, 我想写一个demo code用来练习。于是想到我要相加两个数,无论是i8, i32, isize或是float类型。 于是很自然的想到如下实现,结果报错 :
error[E0369]: cannot add `T` to `T`
编译为什么会报错呢?
1 fn add<T>(a:T,b:T) -> T {
2 let c = a + b;
3 c
4 }
5
6 fn main(){
7 let result = add(3,2);
8
9 println!("add result : {}",result);
10 }
在解释这个问题之前,我们可以这样假设,泛型是一种抽象,虽然我们传入的是基本类型,但是架不住调用这个函数的人传其它的Type进去。这样一来,比如泛型结构体相加是个什么鬼呢?
1 fn main() { 2 struct Int_a<T>{a:T}; 3 struct Int_b<T>{b:T}; 4 let A:Int_a<T> = {3}; 5 let B:Int_b<T> = {2}; 6 add(A,B); 7 }
既然泛型无法相加,那基本类型的泛型相加应该要如何实现呢? 如下,无符号整形,有符号整形,浮点类型相加 :
1 add(3,2); 2 add(3.1,2,3); 3 add(5,-10);
首先映入脑海的是,如果利用泛型约束,是不是就可以做到了呢。为此,我们可以测试如下,结果编译又报错 :
error[E0404]: expected trait, found builtin type `i8`
1 fn add<T:i8>(a:T,b:T) -> T { 2 let mut c = a + b; 3 c 4 } 5 6 fn main(){ 7 let result = add(3,2); 8 9 println!("add result : {}",result); 10 11 let result = add(5.2,3.3); 12 println!("add result float :{}",result); 13 }
好吧,在《Rust by example》中,我们看到这样一句话 :When working with generics, the type parameters often must use traits as bounds to stipulate what functionality a type implements
必须要使用Trait做为约束才可以。 为什么会有这种规定呢? 按我的理解,如果用具体类型做为约束,那泛型完全失去了抽象的能力,上面的函数退化为如下,这样就没有意义了 :
1 fn add (a:i8,b:i8) -> i8 { 2 let mut c = a + b; 3 c 4 }
至此, 还是没能解决我们泛型相加的问题. 不过,Rust错误信息给了我们一个提示,泛型需要使用trait约束,那我们就声明一个trait作为约束好了。很自然的我们能想到,要怎样定义trait才能达到约束的目的。我们只想限定基本类型,那就是说只有实现了Add trait的类型才能作为我们的泛型约束。
1 trait Add { 2 type Output; 3 } 4 impl Add for i8{ 5 type Output = i8; 6 } 7 impl Add for i16{ 8 type Output = i16; 9 } 10 impl Add for u32{ 11 type Output = u32; 12 } 13 impl Add for usize{ 14 type Output = usize; 15 } 16 17 fn add<T:Add<Output=T>>(a:T,b:T) -> T { 18 let mut c = a + b; 19 c 20 } 21 22 fn main(){ 23 let result = add(3i8,2i8); 24 25 println!("add result i8: {}",result); 26 27 let result = add(5u32,3u32); 28 println!("add result u32 :{}",result); 29 }
很不幸,这样还是报错:
error[E0369]: cannot add `T` to `T`
help: consider further restricting this bound
fn add<T:Add<Output=T> + std::ops::Add<Output = T>>(a:T,b:T) -> T {
help: consider further restricting this bound
fn add<T:Add<Output=T> + std::ops::Add<Output = T>>(a:T,b:T) -> T {
按照提示,添加std::ops::Add后,编译通过,测试结果无误! 只是没有明白为什么要用std::ops::Add限定才能编译过呢? 这个问题留待后面想清楚后再写。
1 fn add<T:Add<Output=T>+std::ops::Add<Output = T>>(a:T,b:T) -> T { 2 let c = a + b; 3 c 4 }