Rust中Box类型的协方差

问题描述 投票:2回答:1

在我阅读the subtyping chapter of the 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);
}

(Qazxswpoi)

Playground

error[E0308]: mismatched types --> src/main.rs:13:9 | 13 | foo(b); | ^ expected trait `A`, found trait `B` | = note: expected type `std::boxed::Box<(dyn A + 'static)>` found type `std::boxed::Box<dyn B>` 显然是B的“亚型”,A在其输入上是协变的。我不知道为什么它不起作用或为什么它不会做任何类型的强制。为什么他们认为Box是协变的,唯一的用例是不变量?

rust covariance
1个回答
4
投票

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

Nomicon不是一个完全抛光的文件。目前,该回购中最近的10个问题中有5个专门处理基于其标题的子类型或差异。 Nomicon中的概念可能需要大量工作,但信息通常存在。

首先,查看一些初始段落(强调我的):

Rust中的子类型与其他语言的子类型略有不同。这使得提供简单示例变得更加困难,这是一个问题,因为子类型,尤其是方差,已经很难正确理解。

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

然后它继续显示一些基于特征的代码。重申一点,这段代码不再是Rust代码了;特征不会在Rust中形成子类型!

稍后,有这样的引用:

首先,基于生命周期的子类型引用是Rust中子类型的全部要点。我们进行子类型化的唯一原因是我们可以通过长期存在的东西来预期短期事物。

Rust的子类型概念仅适用于生命周期。

什么是子类型和方差的例子?

Variant lifetimes

这是一个在Box<T>内工作的生命周期的子类型和方差的例子。

一个失败的案例

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

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

一个工作案例

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

Invariant lifetimes

这是一个有效的案例:

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

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

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

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);
}
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);
}

解决具体问题

error[E0597]: `x` does not live long enough --> src/lib.rs:7:16 | 7 | smaller(v, &mut x); | -----------^^^^^^- | | | | | borrowed value does not live long enough | argument requires that `x` is borrowed for `'static` 8 | } | - `x` dropped here while still borrowed 显然是B的“亚型”

它不是。

A对其输入是协变的

它是,协方差只适用于生命周期。

我不知道为什么它不起作用或为什么它不会做任何类型的强制。

这由Box涵盖

为什么他们会认为Why doesn't Rust support trait object upcasting?是协变的

因为它适用于Rust中应用方差的东西。

也可以看看

© www.soinside.com 2019 - 2024. All rights reserved.