子类型和变型
子类型化是隐式的,可以在类型检查或推断的任何阶段发生。子类型化仅限于两种情况:生命周期方面的变型以及具有更高等级生命周期的类型之间的变型。如果我们从类型中擦除生命周期,那么唯一的子类型化将是由于类型相等。
考虑以下示例:字符串字面量始终具有 'static
生命周期。尽管如此,我们可以将 s
赋值给 t
#![allow(unused)] fn main() { fn bar<'a>() { let s: &'static str = "hi"; let t: &'a str = s; } }
由于 'static
比生命周期参数 'a
活得更久,因此 &'static str
是 &'a str
的子类型。
更高等级的 函数指针 和 特征对象 具有另一种子类型关系。它们是由更高等级生命周期的替换所给出的类型的子类型。一些例子
#![allow(unused)] fn main() { // Here 'a is substituted for 'static let subtype: &(for<'a> fn(&'a i32) -> &'a i32) = &((|x| x) as fn(&_) -> &_); let supertype: &(fn(&'static i32) -> &'static i32) = subtype; // This works similarly for trait objects let subtype: &(dyn for<'a> Fn(&'a i32) -> &'a i32) = &|x| x; let supertype: &(dyn Fn(&'static i32) -> &'static i32) = subtype; // We can also substitute one higher-ranked lifetime for another let subtype: &(for<'a, 'b> fn(&'a i32, &'b i32))= &((|x, y| {}) as fn(&_, &_)); let supertype: &for<'c> fn(&'c i32, &'c i32) = subtype; }
变型
变型是泛型类型相对于其参数的属性。泛型类型在参数中的_变型_是指参数的子类型化如何影响类型的子类型化。
- 如果
T
是U
的子类型意味着F<T>
是F<U>
的子类型(子类型化“传递”),则F<T>
对T
是_协变_的 - 如果
T
是U
的子类型意味着F<U>
是F<T>
的子类型,则F<T>
对T
是_逆变_的 - 否则,
F<T>
对T
是_不变_的(无法导出子类型化关系)
类型的变型自动确定如下
类型 | 在 'a 中的变型 | 在 T 中的变型 |
---|---|---|
&'a T | 协变 | 协变 |
&'a mut T | 协变 | 不变 |
*const T | 协变 | |
*mut T | 不变 | |
[T] 和 [T; n] | 协变 | |
fn() -> T | 协变 | |
fn(T) -> () | 逆变 | |
std::cell::UnsafeCell<T> | 不变 | |
std::marker::PhantomData<T> | 协变 | |
dyn Trait<T> + 'a | 协变 | 不变 |
其他 struct
、enum
和 union
类型的变型是通过查看其字段类型的变型来决定的。如果参数在具有不同变型的位
#![allow(unused)] fn main() { use std::cell::UnsafeCell; struct Variance<'a, 'b, 'c, T, U: 'a> { x: &'a U, // This makes `Variance` covariant in 'a, and would // make it covariant in U, but U is used later y: *const T, // Covariant in T z: UnsafeCell<&'b f64>, // Invariant in 'b w: *mut U, // Invariant in U, makes the whole struct invariant f: fn(&'c ()) -> &'c () // Both co- and contravariant, makes 'c invariant // in the struct. } }
当在 struct
、enum
或 union
之外使用时,将在每个位置分别检查参数的变型。
#![allow(unused)] fn main() { use std::cell::UnsafeCell; fn generic_tuple<'short, 'long: 'short>( // 'long is used inside of a tuple in both a co- and invariant position. x: (&'long u32, UnsafeCell<&'long u32>), ) { // As the variance at these positions is computed separately, // we can freely shrink 'long in the covariant position. let _: (&'short u32, UnsafeCell<&'long u32>) = x; } fn takes_fn_ptr<'short, 'middle: 'short>( // 'middle is used in both a co- and contravariant position. f: fn(&'middle ()) -> &'middle (), ) { // As the variance at these positions is computed separately, // we can freely shrink 'middle in the covariant position // and extend it in the contravariant position. let _: fn(&'static ()) -> &'short () = f; } }