子类型和变型

子类型化是隐式的,可以在类型检查或推断的任何阶段发生。子类型化仅限于两种情况:生命周期方面的变型以及具有更高等级生命周期的类型之间的变型。如果我们从类型中擦除生命周期,那么唯一的子类型化将是由于类型相等。

考虑以下示例:字符串字面量始终具有 '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;
}

变型

变型是泛型类型相对于其参数的属性。泛型类型在参数中的_变型_是指参数的子类型化如何影响类型的子类型化。

  • 如果 TU 的子类型意味着 F<T>F<U> 的子类型(子类型化“传递”),则 F<T>T 是_协变_的
  • 如果 TU 的子类型意味着 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协变不变

其他 structenumunion 类型的变型是通过查看其字段类型的变型来决定的。如果参数在具有不同变型的位

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

当在 structenumunion 之外使用时,将在每个位置分别检查参数的变型。

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