Trait 和 lifetime 约束

语法
类型参数约束组 :
   类型参数约束 ( + 类型参数约束 )* +?

类型参数约束 :
      生命周期 | Trait约束

Trait约束 :
      ?? For生命周期? 类型路径
   | ( ?? For生命周期? 类型路径 )

生命周期约束 :
   ( 生命周期 + )* 生命周期?

生命周期 :
      生命周期或标签
   | 'static
   | '_

Trait 和生命周期约束提供了一种方式,以限制 泛型条目 可以使用哪些类型和生命周期作为参数。 可以在 where 从句 提供对类型的约束。 对于一些常见情况,也有更简短的形式:

  • 在声明 泛型参数 之后进行约束: fn f<A: Copy>() {} 等同于 fn f<A>() where A: Copy {}
  • 在 trait 声明中作为 父级trait : trait Circle : Shape {} 等同于 trait Circle where Self : Shape {}
  • 在 trait 声明中作为 关联类型 的约束: trait A { type B: Copy; } 等同于 trait A where Self::B: Copy { type B; }

在使用条目时必须满足条目上的约束。在对泛型条目进行类型检查和借用检查时,可以使用约束来确定类型是否实现了 trait 。例如,给定 Ty: Trait

  • 在泛型函数体中,可以在 Ty 值上调用 Trait 的方法。同样,可以使用 Trait 上的关联常量。
  • 可以使用 Trait 的关联类型。
  • 可以将具有 T: Trait 约束的泛型函数和类型用于 T 使用 Ty
#![allow(unused)]
fn main() {
type Surface = i32;
trait Shape {
    fn draw(&self, surface: Surface);
    fn name() -> &'static str;
}

fn draw_twice<T: Shape>(surface: Surface, sh: T) {
    sh.draw(surface);           // 可以调用方法,因为T: Shape
    sh.draw(surface);
}

fn copy_and_draw_twice<T: Copy>(surface: Surface, sh: T) where T: Shape {
    let shape_copy = sh;        // 不移动 sh ,因为T: Copy
    draw_twice(surface, sh);    // 可以使用泛型函数,因为T: Shape
}

struct Figure<S: Shape>(S, S);

fn name_figure<U: Shape>(
    figure: Figure<U>,          // 类型 Figure<U> 是 well-formed ,因为U: Shape
) {
    println!(
        "Figure of two {}",
        U::name(),              // 可以使用关联函数
    );
}
}

当定义条目时,不使用条目参数或 高阶生命周期 的约束将被检查。 这样的约束如果为 false ,则会报错。

对于某些泛型类型,在使用该条目时还会检查 CopyCloneSized 约束,即使使用时没有提供具体类型。 在可变引用、 trait 对象切片 上将 CopyClone 作为约束是错误的。 在 trait 对象切片 上使用 Sized 作为约束也是错误的。

#![allow(unused)]
fn main() {
struct A<'a, T>
where
    i32: Default,           // 允许,但不实用
    i32: Iterator,          // 错误: `i32` 不是迭代器
    &'a mut T: Copy,        // (在使用时) 错误:无法满足 trait 约束
    [T]: Sized,             // (在使用时) 错误:大小无法在编译时确定
{
    f: &'a T,
}
struct UsesA<'a, T>(A<'a, T>);
}

Trait 和 生命周期约束还用于命名 trait 对象

?Sized

? 仅用于放宽 类型参数关联类型 隐含的 Sized trait 约束。 ?Sized 不能用作其他类型的约束。

生命周期约束

生命周期约束可以应用于类型或其他生命周期。约束 'a: 'b 通常被读作 'a 'b 活得更久。 'a: 'b 意味着 'a 至少和 'b 一样长,因此当 &'b () 有效时,引用 &'a () 也是有效的。

#![allow(unused)]
fn main() {
fn f<'a, 'b>(x: &'a i32, mut y: &'b i32) where 'a: 'b {
    y = x;                      // `&'a i32` 是 `&'b i32` 的子类型,因为 `'a: 'b` 。
    let r: &'b &'a i32 = &&0;   // `&'b &'a i32` 是良好形式的,因为 `'a: 'b` 。
}
}

T: 'a 表示 T 的所有生命周期参数都比 'a 更长。 例如,如果 'a 是一个未约束的生命周期参数,那么 i32: 'static&'static str: 'a 是符合条件的,但是 Vec<&'a ()>: 'static 不符合条件。

高阶 trait 约束

For生命周期 :
   for 泛型参数组

Trait 约束可以对生命周期进行 提阶 ,该约束指示 对于所有 生命周期都成立的约束。 例如, for<'a> &'a T: PartialEq<i32> 这样的约束需要一个这样的实现:

#![allow(unused)]
fn main() {
struct T;
impl<'a> PartialEq<i32> for &'a T {
    // ...
   fn eq(&self, other: &i32) -> bool {true}
}
}

可以用它来将一个 &'a T 的生命周期与任意的 i32 进行比较。

仅一个更高阶的约束可以在此使用,因为引用的生命周期比函数上可能存在的任何生命周期参数都要短:

#![allow(unused)]
fn main() {
fn call_on_ref_zero<F>(f: F) where for<'a> F: Fn(&'a i32) {
    let zero = 0;
    f(&zero);
}
}

高阶的生命周期也可以在 trait 前面指定: 唯一的区别是生命周期参数的作用范围仅限于后面 trait 的末尾,而不是整个约束。这个函数与上一个函数是等价的。

#![allow(unused)]
fn main() {
fn call_on_ref_zero<F>(f: F) where F: for<'a> Fn(&'a i32) {
    let zero = 0;
    f(&zero);
}
}