Могу ли я заставить черту быть ковариантной? - PullRequest
0 голосов
/ 28 декабря 2018

Благодаря @ 1001 * превосходному ответу @ francis-gagné на другой вопрос у меня есть более четкое представление о том, как работает дисперсия.Например, тип, содержащий ссылку, является ковариантным по своему параметру времени жизни, как показано ниже.

struct Foo<'a> (PhantomData<&'a str>);

/// Foo is covariant over its lifetime parameter
pub fn test_foo<'a:'b, 'b:'c, 'c>() {
    let fa: Foo<'a> = Foo(PhantomData);
    let fb: Foo<'b> = Foo(PhantomData);
    let fc: Foo<'c> = Foo(PhantomData);

    let v: Vec<Foo<'b>> = vec![fa, fb]; // fc is not accepted
}

С другой стороны, функция, принимающая ссылку (или тип, содержащий ее), является контравариантной по своему параметру времени жизни..

struct Bar<'a> (PhantomData<fn(&'a str)>);

/// Bar is contravariant over its lifetime parameter
pub fn test_bar<'a:'b, 'b:'c, 'c>() {
    let ba: Bar<'a> = Bar(PhantomData);
    let bb: Bar<'b> = Bar(PhantomData);
    let bc: Bar<'c> = Bar(PhantomData);

    let v: Vec<Bar<'b>> = vec![bb, bc]; // ba is not accepted
}

Наконец, признак с параметром времени жизни инвариантен относительно своего параметра времени жизни.

pub trait Baz<'a> {}

impl<'a> Baz<'a> for () {}

/// Baz is invariant over its lifetime parameter
pub fn test_baz<'a:'b, 'b:'c, 'c>() {
    let za: Box<dyn Baz<'a>> = Box::new(());
    let zb: Box<dyn Baz<'b>> = Box::new(());
    let zc: Box<dyn Baz<'c>> = Box::new(());

    let v: Vec<Box<dyn Baz<'b>>> = vec![zb]; // za and zx are not accepted
}

Это имеет смысл, поскольку черта может быть реализованакак ковариантным, так и контравариантным типом, как показано ниже.

impl<'a> Baz<'a> for Foo<'a> {}
impl<'a> Baz<'a> for Bar<'a> {}

Мой вопрос: могу ли я заставить черту быть ковариантной в течение ее параметра времени жизни?Я ожидаю, что черта маркера, такая как:

trait Baz<'a>: Covariant<'a> {}

, сделает незаконной реализацию этой черты с контравариантным типом и позволит za быть членом вектора v в test_baz функция выше.

Конечно, возможность сделать обратное (заставить черту быть противоположной) также может быть полезна ...

Примеры на игровой площадке

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Я нашел обходной путь.Вместо того, чтобы отмечать черту как ковариантную (что, как заметил @trentcl, невозможно в Rust 1.31), я заставил тип реализовать черту для всех жизней, меньших его собственной:

impl<'a:'b, 'b> Baz<'b> for Foo<'a> {}

Таким образом,Я могу использовать экземпляры Foo<'b> и Foo<'a> всякий раз, когда требуется Bar<'b>:

pub fn test_baz<'a:'b, 'b:'c, 'c>() {
    let fa: Foo<'a> = Foo(PhantomData);
    let fb: Foo<'b> = Foo(PhantomData);
    let fc: Foo<'c> = Foo(PhantomData);

    let v: Vec<&dyn Baz<'b>> = vec![&fa, &fb]; // &fc is not accepted
}

Конечно, для этого требуется, чтобы каждый исполнитель черты следовал этому шаблону,поэтому он не так силен, как пометка самой черты ковариантной.Но в некоторых ситуациях это может помочь.

Пример на игровой площадке

0 голосов
/ 29 декабря 2018

Нет.

Вы можете выразить "значение, которое реализует Baz<'x> для любого 'x":

pub fn test_baz<'a:'b, 'b:'c, 'c>() {
    let za: Box<dyn for<'x> Baz<'x>> = Box::new(());
    let zb: Box<dyn for<'x> Baz<'x>> = Box::new(());
    let zc: Box<dyn for<'x> Baz<'x>> = Box::new(());

    let v: Vec<Box<dyn for<'x> Baz<'x>>> = vec![za, zb, zc];
}

Но вы не можете (с Rust 1.31) напишите Box<dyn for<'x: 'b> Baz<'x>>, и даже если бы вы могли, этот синтаксис работал бы только на протяжении жизни;это не позволит вам выразить ковариацию по параметрам типа.

...