Rust的trait的另外一个大用处是,作为泛型约束使用。关于泛型,本书第三部
分还会详细解释。下面用一个简单示例演示一下trait如何作为泛型约束使用: use std::fmt::Debug;
fn my_print(x: T) {
println!(“The value is {:?}.”, x);
}
fn main() {
my_print(“China”);
my_print(41_i32);
my_print(true);
my_print([‘a’, ‘b’, ‘c’])
}
上面这段代码中,my_print函数引入了一个泛型参数T,所以它的参数不是一个 具体类型,而是一组类型。冒号后面加trait名字,就是这个泛型参数的约束条件。 它要求这个T类型实现Debug这个trait。这是因为我们在函数体内,用到了println! 格式化打印,而且用了{:?}这样的格式控制符,它要求类型满足Debug的约束, 否则编译不过。
在调用的时候,凡是满足Debug约束的类型都可以是这个函数的参数,所以我 们可以看到以上四种调用都是可以编译通过的。假如我们自定义一个类型,而它没 有实现Debug trait,我们就会发现,用这个类型作为my_print的参数的话,编译就会 报错。
所以,泛型约束既是对实现部分的约束,也是对调用部分的约束。 泛型约束还有另外一种写法,即where子句。示例如下:
fn my_print(x: T) where T: Debug {
println!(“The value is {:?}.”, x);
}
对于这种简单的情况,两种写法都可以。但是在某些复杂的情况下,泛型约束 只有where子句可以表达,泛型参数后面直接加冒号的写法表达不出来,比如涉及 关联类型的时候,请参见第21章。
trait允许继承。类似下面这样: trait Base { … }
trait Derived : Base { … }
这表示Derived trait继承了Base trait。它表达的意思是,满足Derived的类型,必 然也满足Base trait。所以,我们在针对一个具体类型impl Derived的时候,编译器也 会要求我们同时impl Base。示例如下:
trait Base {}
trait Derived : Base {}

struct T;
impl Derived for T {}
fn main() { }
编译,出现错误,提示信息为:
–> test.rs:7:6
|
7 | impl Derived for T {}
| ^^^^^^^ the trait Base is not implemented for T
我们再加上一句
impl Base for T {}
编译器就不再报错了。

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐