静态类型语言黑话-协变
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 协议 ,转载请注明出处!