结论:生命周期子类型规则是 'long: 'short——活得久的引用可以用在要求活得短的地方。型变决定容器类型是否继承这种替换关系:&'a T'aT 协变;&'a mut T'a 协变但对 T 不变(invariant);函数类型对参数逆变、对返回值协变。

展开:协变意味着 &'static str 可以当 &'a str 用,传长命引用给短命参数没问题。&mut TT 必须不变:若允许协变,就能把 &mut &'static str 当作 &mut &'a str 使用,然后往里写入一个只活 'a 的引用,造成悬垂。UnsafeCell 及其之上的 Cell/Mutex 也是不变的,这是内部可变性安全的关键。易错点:手写 PhantomData 或 unsafe 封装时要选对 marker(PhantomData<&'a T> 协变、PhantomData<fn(T)> 逆变),否则编译器会做错误的型变假设,可能放过悬垂引用。追问方向:为什么 Vec<T>T 协变而 Cell<T> 不变?Vec 没有"在任意处整体替换元素类型"的通道,Cell 有。

fn shorten<'a>(r: &'static str) -> &'a str { r } // 协变:'static 可作任意 'a
// 若 &mut 对 T 协变,下面的写入将制造悬垂,不变性使其编译失败:
// fn bad<'a>(slot: &mut &'a str, s: &'a str) { *slot = s; }