Trait 和生命周期约束

语法
TypeParamBounds :
   TypeParamBound ( + TypeParamBound )* +?

TypeParamBound :
      Lifetime | TraitBound | UseBound

TraitBound :
      ( ? | ForLifetimes )? TypePath
   | ( ( ? | ForLifetimes )? TypePath )

LifetimeBounds :
   ( Lifetime + )* Lifetime?

Lifetime :
      LIFETIME_OR_LABEL
   | 'static
   | '_

UseBound :
   use UseBoundGenericArgs

UseBoundGenericArgs :
      < >
   | <
      ( UseBoundGenericArg ,)*
      UseBoundGenericArg ,?
      >

UseBoundGenericArg :
      Lifetime
   | IDENTIFIER
   | Self

Trait 和生命周期约束为泛型项提供了一种限制哪些类型和生命周期可以用作其参数的方法。约束可以为 where 子句中的任何类型提供。对于某些常见情况,也有更简短的形式

  • 在声明泛型参数后编写的约束:fn f<A: Copy>() {}fn f<A>() where A: Copy {} 相同。
  • 在 trait 声明中作为超 traittrait 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 约束的泛型函数和类型可以与用于 TTy 一起使用。
#![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);           // Can call method because T: Shape
    sh.draw(surface);
}

fn copy_and_draw_twice<T: Copy>(surface: Surface, sh: T) where T: Shape {
    let shape_copy = sh;        // doesn't move sh because T: Copy
    draw_twice(surface, sh);    // Can use generic function because T: Shape
}

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

fn name_figure<U: Shape>(
    figure: Figure<U>,          // Type Figure<U> is well-formed because U: Shape
) {
    println!(
        "Figure of two {}",
        U::name(),              // Can use associated function
    );
}
}

不使用项的参数或高阶生命周期的约束在定义项时进行检查。如果这样的约束为假,则会报错。

CopyCloneSized 约束也会在使用项时针对某些泛型类型进行检查,即使使用没有提供具体类型。在可变引用、trait 对象切片上使用 CopyClone 作为约束是错误的。在 trait 对象或切片上使用 Sized 作为约束是错误的。

#![allow(unused)]
fn main() {
struct A<'a, T>
where
    i32: Default,           // Allowed, but not useful
    i32: Iterator,          // Error: `i32` is not an iterator
    &'a mut T: Copy,        // (at use) Error: the trait bound is not satisfied
    [T]: Sized,             // (at use) Error: size cannot be known at compilation
{
    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 is a subtype of &'b i32 because 'a: 'b
    let r: &'b &'a i32 = &&0;   // &'b &'a i32 is well formed because 'a: 'b
}
}

T: 'a 表示 T 的所有生命周期参数都比 'a 活得更久。例如,如果 'a 是一个无约束的生命周期参数,则 i32: 'static&'static str: 'a 满足条件,但 Vec<&'a ()>: 'static 不满足条件。

高阶 trait 约束

ForLifetimes :
   for GenericParams

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 Ti32

这里只能使用高阶约束,因为引用的生命周期比函数上任何可能的生命周期参数都短

#![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);
}
}

隐含约束

类型良好形式所需的生命周期约束有时是推断出来的。

#![allow(unused)]
fn main() {
fn requires_t_outlives_a<'a, T>(x: &'a T) {}
}

类型参数 T 需要比 'a 活得更久,类型 &'a T 才能成为良好形式。这是推断出来的,因为函数签名包含类型 &'a T,只有在 T: 'a 成立时才有效。

隐含约束会为函数的所有参数和输出添加。在 requires_t_outlives_a 内部,即使您没有显式指定 T: 'a,您也可以假设它成立

#![allow(unused)]
fn main() {
fn requires_t_outlives_a_not_implied<'a, T: 'a>() {}

fn requires_t_outlives_a<'a, T>(x: &'a T) {
    // This compiles, because `T: 'a` is implied by
    // the reference type `&'a T`.
    requires_t_outlives_a_not_implied::<'a, T>();
}
}
#![allow(unused)]
fn main() {
fn requires_t_outlives_a_not_implied<'a, T: 'a>() {}
fn not_implied<'a, T>() {
    // This errors, because `T: 'a` is not implied by
    // the function signature.
    requires_t_outlives_a_not_implied::<'a, T>();
}
}

仅隐含生命周期约束,trait 约束仍然必须显式添加。因此,以下示例会导致错误

#![allow(unused)]
fn main() {
use std::fmt::Debug;
struct IsDebug<T: Debug>(T);
// error[E0277]: `T` doesn't implement `Debug`
fn doesnt_specify_t_debug<T>(x: IsDebug<T>) {}
}

生命周期约束也会为类型定义和 impl 块为任何类型推断出来

#![allow(unused)]
fn main() {
struct Struct<'a, T> {
    // This requires `T: 'a` to be well-formed
    // which is inferred by the compiler.
    field: &'a T,
}

enum Enum<'a, T> {
    // This requires `T: 'a` to be well-formed,
    // which is inferred by the compiler.
    //
    // Note that `T: 'a` is required even when only
    // using `Enum::OtherVariant`.
    SomeVariant(&'a T),
    OtherVariant,
}

trait Trait<'a, T: 'a> {}

// This would error because `T: 'a` is not implied by any type
// in the impl header.
//     impl<'a, T> Trait<'a, T> for () {}

// This compiles as `T: 'a` is implied by the self type `&'a T`.
impl<'a, T> Trait<'a, T> for &'a T {}
}

Use 约束

某些约束列表可能包含 use<..> 约束,以控制哪些泛型参数被 impl Trait 抽象返回类型捕获。有关更多详细信息,请参见 精确捕获