Rust中Box类型的协方差 [英] Covariance of Box type in Rust

查看:151
本文介绍了Rust中Box类型的协方差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读 Nomicon的子类型一章后,我无法不要把我的头放在类型参数的协方差周围。特别是对于 Box< T> 类型,它被描述为: T是协变的

After I read the subtyping chapter of the Nomicon, I couldn't wrap my head around covariance of a type parameter. Especially for the Box<T> type, which is described as: T is covariant.

但是,如果我编写这段代码:

However, if I write this code:

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

游乐场

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 显然是<$ c的子类型 $ c> A 和 Box 在其输入上是协变的。我不知道为什么它不起作用或为什么它不执行任何类型的强制转换。他们为什么会认为 Box< T> 是协变的,而唯一的用例是不变的?

B is clearly a "subtype" of A and 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 would they consider Box<T> to be covariant where the only use cases are invariants?

推荐答案

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



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

What subtyping and variance means in Rust

The Nomicon is not a fully polished document. Right now, 5 of the most recent 10 issues in that repo specifically deal with subtyping or variance based on their title alone. The concepts in the Nomicon can require substantial effort, but the information is generally there.

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

First off, check out some initial paragraphs (emphasis mine):


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

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.

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

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中形成子类型!

It then goes on to show some trait-based code. Reiterating the point, this code is not Rust code anymore; traits do not form subtypes in 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的子类型化概念仅适用于生命周期

Rust's notion of subtyping only applies to lifetimes.

这是 Box 内的子类型和寿命变化示例。

Here's an example of subtyping and variance of lifetimes at work inside of a 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)
}



不变寿命



在这种情况下有效:

Invariant lifetimes

Here's a case that works:

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

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

The same code with all the references changed to mutable references will fail because mutable references are invariant:

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



解决特定问题



Addressing specific points


B 显然是 A 的子类型

不是。


Box 在输入上是协变的。

它是协变仅适用于生命周期的地方。

It is, where covariance is only applicable to lifetimes.


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

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

为什么Rust不支持特征对象向上投射?


为什么他们会考虑 Box< ; T> 是协变的

因为是,对于Rust中适用于方差的事物。

Because it is, for the things in Rust to which variance is applied.

  • How do I deal with wrapper type invariance in Rust?
  • Why does linking lifetimes matter only with mutable references?
  • What is an example of contravariant use in Rust?

这篇关于Rust中Box类型的协方差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆