Ковариантность типа Box в Rust - PullRequest
2 голосов
/ 16 марта 2019

После того, как я прочитал главу о подтипах в 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);
}

( Детская площадка )

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> ковариантным, когда единственными вариантами использования являются инварианты?

1 Ответ

4 голосов
/ 17 марта 2019

Что означает подтип и дисперсия в Rust

Nomicon - не полностью отшлифованный документ. Прямо сейчас, 5 из 10 последних выпусков в этом репо конкретно касаются подтипов или отклонений, основанных только на их названии. Концепции в Nomicon могут потребовать значительных усилий, но информация, как правило, там.

Прежде всего, посмотрите некоторые начальные абзацы (выделено мной):

Подтипирование в Rust немного отличается от подтипирования в других языках. Это затрудняет приведение простых примеров, что является проблемой, поскольку подтипы, и особенно дисперсию, уже трудно понять должным образом.

Для простоты в этом разделе будет рассмотрено небольшое расширение для языка Rust , которое добавляет новые и более простые отношения подтипов. После определения концепций и проблем в рамках этой более простой системы мы свяжем ее с тем, как на самом деле происходит подтип в Rust.

Затем он показывает код, основанный на особенностях. Повторяя точку зрения, этот код не код Rust больше; черты не образуют подтипы в Rust!

Позже есть эта цитата:

Прежде всего, ссылки на подтипы, основанные на их времени жизни, - это вся точка подтипа в Rust. Единственная причина, по которой у нас есть подтипы, заключается в том, что мы можем передавать долгоживущие вещи там, где ожидаются недолговечные вещи.

Понятие подтипа 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 явно является «подтипом» A

Это не так.

Box является ковариантным по своему входу

Это когда ковариация применима только к жизням.

Я не знаю, почему это не работает или почему оно не приводит к принуждению.

Это покрыто Почему Rust не поддерживает вытеснение объекта черты?

Почему они считают Box<T> ковариантным

Потому что это так, для вещей в Rust, к которым применяется дисперсия.

Смотри также

...