Rust中Box类型的协方差

LVB*_*LVB 3 covariance rust

在阅读Nomicon的子类型章节之后,我就无法将类型参数的协方差包住。特别是对于Box<T>类型,它被描述为:T is covariant

但是,如果我编写此代码:

trait A {}
trait B: A {}

struct C;
impl A for C {}
impl B for C {}

fn foo(v: Box<A>) {}

fn main() {
    let c = C;
    let b: Box<B> = Box::new(c);
    foo(b);
}
Run Code Online (Sandbox Code Playgroud)

游乐场

trait A {}
trait B: A {}

struct C;
impl A for C {}
impl B for C {}

fn foo(v: Box<A>) {}

fn main() {
    let c = C;
    let b: Box<B> = Box::new(c);
    foo(b);
}
Run Code Online (Sandbox Code Playgroud)

B显然是的“子类型”,A并且Box在其输入上是协变的。我不知道为什么它不起作用或为什么它不执行任何类型的强制转换。Box<T>在唯一的用例是不变式的情况下,为什么他们会认为是协变的?

She*_*ter 5

在Rust中子类型化和方差意味着什么

Nomicon不是完全抛光的文档。目前,该回购协议中最近发行的10个问题中有5个专门根据其标题来处理子类型或差异。Nomicon中的概念可能需要大量的精力,但信息通常存在。

首先,请查看一些初始段落(重点是我的):

Rust中的子类型与其他语言中的子类型有些不同。这使得很难给出简单的例子,这是一个问题,因为子类型,尤其是方差,已经很难正确理解。

为了使事情简单,本节将考虑对Rust语言的一个小扩展,它增加了新的和更简单的子类型关系。在这个简单的系统下建立概念和问题之后,我们将把它与Rust中子类型的实际发生方式联系起来。

然后继续显示一些基于特征的代码。重申一点,这个代码是生锈的代码了; 特质在Rust中不构成子类型!

稍后,有这句话:

首先,基于引用的生存期进行子类型化是Rust 中子类型化全部要点。我们拥有子类型化的唯一原因是,我们可以在预期寿命短的地方传递寿命长的东西。

Rust的子类型化概念仅适用于生存期

子类型化和差异化的例子是什么?

可变寿命

这是一个示例子类型和寿命的变化Box

失败的案例

fn smaller<'a>(v: Box<&'a i32>) {
    bigger(v)
}

fn bigger(v: Box<&'static i32>) {}
Run Code Online (Sandbox Code Playgroud)
fn smaller<'a>(v: Box<&'a i32>) {
    bigger(v)
}

fn bigger(v: Box<&'static i32>) {}
Run Code Online (Sandbox Code Playgroud)

一个工作案例

fn smaller<'a>(v: Box<&'a i32>) {}

fn bigger(v: Box<&'static i32>) {
    smaller(v)
}
Run Code Online (Sandbox Code Playgroud)

不变寿命

这是一个可行的情况:

struct S<'a>(&'a i32);

fn smaller<'a>(_v: &S<'a>, _x: &'a i32) {}

fn bigger(v: &S<'static>) {
    let x: i32 = 1;
    smaller(v, &x);
}
Run Code Online (Sandbox Code Playgroud)

将所有引用更改为可变引用的相同代码将失败,因为可变引用是不变的:

struct S<'a>(&'a mut i32);

fn smaller<'a>(_v: &mut S<'a>, _x: &'a mut i32) {}

fn bigger(v: &mut S<'static>) {
    let mut x: i32 = 1;
    smaller(v, &mut x);
}
Run Code Online (Sandbox Code Playgroud)
error[E0308]: mismatched types
 --> src/lib.rs:2:12
  |
2 |     bigger(v)
  |            ^ lifetime mismatch
  |
  = note: expected type `std::boxed::Box<&'static i32>`
             found type `std::boxed::Box<&'a i32>`
note: the lifetime 'a as defined on the function body at 1:12...
 --> src/lib.rs:1:12
  |
1 | fn smaller<'a>(v: Box<&'a i32>) {
  |            ^^
  = note: ...does not necessarily outlive the static lifetime
Run Code Online (Sandbox Code Playgroud)

解决具体问题

B 显然是的“子类型” A

它不是。

Box 在输入上是协变的

也就是协方差仅适用于寿命。

我不知道为什么它不起作用或为什么它不执行任何类型的强制转换。

这是通过覆盖为什么不生锈特性的支持对象向上转型?

他们为什么会认为Box<T>是协变的

因为是这样,对于Rust中适用于差异的事物。

也可以看看

  • _“ Nomicon并非完全完善的文档。” _ —我个人来说,这整个部分都是很糟糕的,引入假想的语言扩展并不是正确的教学方法。 (2认同)
  • @SvenMarnach取决于我们是否严格使用[dyn关键字](/sf/ask/3545504931/)...在某些情况下上下文`A'确实是一种类型,但是为了最大程度地说明,最好说`dyn A`是这种类型。 (2认同)