初探模板

请确保自己了解过模板或泛型后阅读。

在函数中使用模板

1
2
3
// 模板类型名随意,但是建议符合struct/enum命名规则
fn test<T/* , A, B */>(t: T) {
}

好了,现在你可以对任意类型的数据使用test(t: T)了:

1
2
3
4
5
6
7
fn main() {
//注意,这里我没有写明`T`的类型,因为编译器能推断出类型。
test(12); // 例如这里等价于test::<i32>(12);
test(12.); // test::<f64>(12.);
test('中'); // test::<char>('中');
test([1, 2, 3]); // test::<[i32; 3]>([1, 2, 3]);
}

但是上面的模板函数对参数没有做任何操作呢,不妨试试进行运算:

1
2
3
fn test<T>(t: T) {
t + 1;
}

尝试编译一下,肯定会报错:

1
2
3
4
5
6
7
8
9
10
11
12
error[E0369]: cannot add `{integer}` to `T`
--> src\main.rs:2:7
|
2 | t + 1;
| - ^ - {integer}
| |
| T
|
help: consider restricting type parameter `T`
|
1 | fn test<T: std::ops::Add<Output = {integer}>>(t: T) {
| +++++++++++++++++++++++++++++++++++

肯定有人会想:上面的代码之所以报错,是因为对chararray类型使用了test(t: T)test(t: T)又对t使用了+运算,因而无法编译。这个答案放在C++中是对的,但是放在Rust中则是错的。看看上面的报错原因error[E0369]: cannot add `{integer}` to `T`(不能将属于{integer}的类型加到T上),错误发生在函数上而非类型上。简单的解释是:模板类型T没有实现+运算函数,即使对实现了+运算函数的类型使用test(t: T),也不能编译。意味着,test(t: T)main()里的4行语句都无法编译。

那么,如何让模板类型T实现+运算函数呢?

Rust中,并不能为模板类型T实现+,但是可以要求使用该函数的类型必须实现+,并自己给调用该函数的类型实现+,这样编译器就能确保使用该函数的类型都有+运算。而报错的help信息中提示了相关做法:

1
2
3
4
help: consider restricting type parameter `T`
1 | fn test<T: std::ops::Add<Output = {integer}>>(t: T) {
| +++++++++++++++++++++++++++++++++++
| 在代码中添加上述部分,表示要求使用`test()`的类型`T`必须实现了`std::ops::Add<Output>``trait`,该`trait`包含有`+`函数的声明,因此函数中就能对`T`类型数据进行`+`运算。

让我们先尝试添加上述代码,你会发现,代码依然无法编译,因为{integer}并不是可编译的类型,{integer}是整型类型的统称,包括i8u8...而且,如果你尝试将{integer}改成u8i8之类的整型,代码依然无法编译,具体的原因和解决办法脱离了主题,这里就不细说了,有兴趣的可以在学过下一篇后尝试实现。

回到正题,:是修饰符,表示其前边的模板类型实现了后面列出的traitstd::ops::Add<Output>就是一个trait。至于什么是trait,下篇再详细介绍。

struct中使用模板

例子:

1
2
3
4
5
6
7
8
9
struct Test<A, B> {
ta: A,
tb: B,
}

fn main() {
// 同样,这里编译器也能推断出类型。
let t/* : Test<i32, i32> */ = Test{ta: 1, tb: 2};
}

impl中使用模板

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Test<A, B> {
ta: A,
tb: B,
}

// 需要注意的是,`impl`块和类型后都要列出模板类型列表,类型名必须一致。
// 这两个模板类型列表用途不同,下篇再细说。
impl<A, B> Test<A, B> {
fn new(ta: A, tb: B) -> Self {
Test{ta, tb}
}
}

fn main() {
let t/* : Test<i32, i32> */ = Test::new(1, 2);
}