rust - 如果 Box<dyn B> 不是 Box<dyn A> 的子类型,其中 B : A?,这意味着 Box 是协变的是什么意思

标签 rust covariance

读完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<dyn A>) {}

fn main() {
    let c = C;
    let b: Box<dyn B> = Box::new(c);
    foo(b);
}

( 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在其输入上是协变的。我不知道为什么它不起作用或者为什么它不会进行任何类型的强制转换。为什么他们会考虑 Box<T>在唯一用例是不变量的情况下是协变的?

最佳答案

Rust 中的子类型和变体意味着什么

Nomicon 并不是一份完全完善的文件。现在,该 repo 协议(protocol)中最近的 10 期中有 5 期专门根据其标题单独处理子类型或差异。 Nomicon 中的概念可能需要大量努力,但信息通常就在那里。

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

Subtyping in Rust is a bit different from subtyping in other languages. This makes it harder to give simple examples, which is a problem since subtyping, and especially variance, are already hard to understand properly.

To keep things simple, this section will consider a small extension to the Rust language that adds a new and simpler subtyping relationship. After establishing concepts and issues under this simpler system, we will then relate it back to how subtyping actually occurs in Rust.

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

后来,有这句话:

First and foremost, subtyping references based on their lifetimes is the entire point of subtyping in Rust. The only reason we have subtyping is so we can pass long-lived things where short-lived things are expected.

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

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

变体生命周期

这是一个在 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

一个工作案例

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 is clearly a "subtype" of A

不是。

Box is covariant over its input

是的,协方差仅适用于生命周期。

I don't know why it doesn't work or why it won't do any type coercion.

Why doesn't Rust support trait object upcasting? 涵盖了这一点

Why would they consider Box<T> to be covariant

因为它是,对于 Rust 中应用了方差的东西。

另见

关于rust - 如果 Box<dyn B> 不是 Box<dyn A> 的子类型,其中 B : A?,这意味着 Box 是协变的是什么意思,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55200843/

相关文章:

rust - 如何使用一系列参数初始化结构

rust - 什么时候只需要 PartialEq 而不需要 Eq 是合适的?

python - 生成由 N=100 个二维样本组成的数据集

c# - 为什么我不能为在 C# 中具有 IList<IList<int>> 返回类型的函数返回类型为 List<List<int>> 的变量

rust - 无锁堆栈,将 is_empty() 中的 Acquire 替换为 Relaxed

rust - 一个实现什么时候想要在 Rust 中获得 self 的所有权?

gcc - 无法交叉编译 curl-rust 示例

java - 编程语言中的协变和逆变有什么区别?

c# - StackOverflowException 通过使用具有协变泛型参数的显式接口(interface)实现

c# - 受约束泛型类型参数的逆变