静态类型语言黑话-协变
Rust 中的协变和反变
- 协变(Covariant):如果
A是B的子类型(A <: B),那么Container<A>也是Container<B>的子类型。 - 逆变/反变(Contravariant):如果
A是B的子类型(A <: B),那么Function<B>反向成为Function<A>的子类型。
Rust 中的协变
在 Rust 里,如果 T 的生命周期 'a 是
'b 的子生命周期('a <: 'b),那么
&'a T 也是 &'b T 的子类型。这意味着
&'a T 是 协变的(covariant)。
1 | |
协变的意义
- 允许生命周期缩短(即更“短暂”的借用可以替代“长久”的借用)。
- 常见于不可变引用,因为不可变数据不会被修改,所以缩短引用是安全的。
Rust 中 *mut 是 invariant ,不能被子类型化,而 NonNull<T> 是协变的。其实现方式,是通过转换为 *const T 的方式,而 *const T 是协变的。
&'a mut T 对于 'a 来说是协变(
covariant )的,但是对于 T 是不变的(
invariant )。
1 | |
另外,如果使用 NonNull<T> 作为一个类的成员,为了不受到一些间接因素的影响,类型擦除或者其它间接的什么原因,使用一个 _boo: PhantomData<T> 成员,来告诉编译器,NonNull 中一定是一个 T 类型。
Rust 中的反变
在 Rust
里,函数参数的生命周期是逆变(contravariant)
的。这意味着,如果 'a <: 'b,那么
fn(&'b i32) -> () 可以赋值给
fn(&'a i32) -> ()。
1 | |
反变的意义
- 防止不安全的生命周期扩展(即不能让短生命周期的数据被长生命周期的函数引用)。
- 常见于函数指针或闭包,因为调用方可以传递更具体的参数类型,而不影响调用。
C++ 中的协变和反变
在 C++,协变和反变主要体现在继承和模板的上下界规则,特别是 返回值(协变)和参数(逆变) 的继承关系。
C++ 中的协变
如果派生类 Derived 继承了 Base,那么返回
Derived* 的函数可以覆盖返回 Base*
的函数(返回值是协变的)。
1 | |
协变的意义
- 允许更具体的返回类型,使得调用者可以获得更具体的对象,而不需要额外的类型转换。
C++ 中的反变
在 C++ 中,函数参数是逆变的。如果
Derived 继承自 Base,则
void func(Derived*) 不能替代
void func(Base*),但反过来可以。
1 | |
反变的意义
- 防止子类对象被错误地当作父类处理,因为子类可能有额外的字段或行为。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!