特征和生命周期约束
语法
TypeParamBounds :
TypeParamBound (+
TypeParamBound )*+
?TypeParamBound :
Lifetime | TraitBound | UseBoundTraitBound :
(?
| ForLifetimes )? TypePath
|(
(?
| ForLifetimes )? TypePath)
LifetimeBounds :
( Lifetime+
)* Lifetime?Lifetime :
LIFETIME_OR_LABEL
|'static
|'_
UseBound :
use
UseBoundGenericArgsUseBoundGenericArgs :
<
>
|<
( UseBoundGenericArg,
)*
UseBoundGenericArg,
?
>
UseBoundGenericArg :
Lifetime
| IDENTIFIER
|Self
特征和生命周期约束为泛型项提供了一种限制哪些类型和生命周期可以作为其参数使用的方法。约束可以在where 子句中的任何类型上提供。对于某些常见情况,也有较短的形式
- 在声明泛型参数后编写的约束:
fn f<A: Copy>() {}
与fn f<A>() where A: Copy {}
相同。 - 在特征声明中作为超特征:
trait Circle : Shape {}
等效于trait Circle where Self : Shape {}
。 - 在特征声明中作为对关联类型的约束:
trait A { type B: Copy; }
等效于trait A where Self::B: Copy { type B; }
。
使用某个项时,必须满足该项上的约束。当对泛型项进行类型检查和借用检查时,可以使用约束来确定某个类型是否实现了某个特征。例如,给定 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); // 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 ); } }
在定义项时会检查不使用该项的参数或高阶生命周期的约束。如果这样的约束为假,则会出错。
Copy
、Clone
和 Sized
约束在使用项时,即使使用不提供具体类型的项,也会对某些泛型类型进行检查。在可变引用、特征对象或 切片上使用 Copy
或 Clone
作为约束是错误的。在特征对象或切片上使用 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>); }
特征和生命周期约束也用于命名特征对象。
?Sized
?
仅用于放宽类型参数或关联类型的隐式 Sized
特征约束。?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
则不满足。
高阶特征约束
ForLifetimes :
for
GenericParams
特征约束可以是生命周期上的高阶。这些约束指定了一个对于所有生命周期都为真的约束。例如,像 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); } }
高阶生命周期也可以在特征之前指定:唯一的区别是生命周期参数的作用域,它只延伸到后续特征的末尾,而不是整个约束。此函数与最后一个函数等效。
#![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>(); } }
仅暗示生命周期约束,特征约束仍然必须显式添加。因此,以下示例会导致错误
#![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
抽象返回类型捕获哪些泛型参数。有关更多详细信息,请参见精确捕获。