Rust的trait的另外一个大用处是,作为泛型约束使用。关于泛型,

分还会详细解释。下面用一个简单示例演示一下trait如何作为泛型约束使用: use std::fmt::Debug;

  fn my_print<T : Debug>(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<T>(x: T) where T: Debug {

      println!("The value is {:?}.", x);

}

对于这种简单的情况,两种写法都可以。但是在某些复杂的情况下,泛型约束 只有where子句可以表达,泛型参数后面直接加冒号的写法表达不出来,比如涉及 关联类型的时候。

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 {}

编译器就不再报错了。