Как реализовать черту для типа с «арендованной» ссылкой - PullRequest
0 голосов
/ 03 сентября 2018

Примечание: я постарался сделать этот пост максимально сжатым, полный код можно найти по адресу https://github.com/pchampin/pair_trait.

Проблема

Я определил следующую черту:

pub trait Pair {
    type Item: Borrow<str>;
    fn first(&self) -> &Self::Item;
    fn second(&self) -> &Self::Item;
}

У меня есть общие реализации этой черты для (T,T) и [T;2] для любого T, реализующего Borrow<str>.

У меня также есть тип, построенный из проката ящика, содержащего String и два Cow<str>, которые заимствованы из этой строки:

#[rental(covariant)]
pub struct SelfSustainedPair {
    line: String,
    pair: (Cow<'line, str>, Cow<'line, str>),
}

Я бы хотел, чтобы этот тип реализовал черту Pair, описанную выше, но я не могу найти способ сделать это.

Попытка # 0

impl SelfSustainedPair {
    pub fn first(&self) -> &Cow<str> { self.suffix().first() }
    pub fn second(&self) -> &Cow<str> { self.suffix().second() }
}

Я знаю, что это не реализация этой черты, но я просто хотел быть уверен, что смогу реализовать методы first и second. Ответ: да, код выше компилируется.

Попытка # 1

impl Pair for SelfSustainedPair {
    type Item = Cow<str>;
    fn first(&self) -> &Cow<str> { self.suffix().first() }
    fn second(&self) -> &Cow<str> { self.suffix().second() }
}

Не удается скомпилировать с сообщением «ожидаемый параметр времени жизни» для 2-й строки.

Это разочаровывает, потому что это так близко к попытке № 0 выше.

Попытка # 2

impl<'a> Pair for SelfSustainedPair {
    type Item = Cow<'a, str>;
    fn first(&self) -> &Cow<'a, str> { self.suffix().first() }
    fn second(&self) -> &Cow<'a, str> { self.suffix().second() }
}

Здесь компилятор жалуется на «неограниченный параметр времени жизни» для первой строки (impl<'a>).

Попытка # 3

Я изменил свой признак Pair, чтобы он ожидал параметр времени жизни. Удивительно, но это работает, даже если параметр продолжительности жизни никогда не используется в определении черты ...

Я тогда написал:

impl<'a> Pair<'a> for SelfSustainedPair {
    type Item = Cow<'a, str>;
    fn first(&self) -> &Cow<'a, str> { self.suffix().first() }
    fn second(&self) -> &Cow<'a, str> { self.suffix().second() }
}

Теперь компилятор жалуется, что он "не может определить подходящее время жизни для autoref" в обоих методах ...

В любом случае, моя интуиция заключается в том, что это неправильный путь: время жизни, для которого возвращаемое Cow не может быть указано независимо от значения self ...

Попытка # 4

В идеале это то, что я хотел бы написать:

impl Pair for SelfSustainedPair {
    type Item = Cow<'self, str>;
    fn first(&self) -> &Cow<str> { self.suffix().first() }
    fn second(&self) -> &Cow<str> { self.suffix().second() }
}

но очевидно, что компилятор не знает о времени жизни self.

1 Ответ

0 голосов
/ 03 сентября 2018

К сожалению, полностью задуманный дизайн в настоящее время недостижим. Тем не менее, мы можем адаптировать попытку № 3 к сортировке работы.

Идея возврата &Self::Item является слегка некорректной, поскольку связанный тип Item уже представляет заимствованную стоимость. Имеет смысл вернуть его напрямую:

pub trait Pair {
    type Item: Borrow<str>;
    fn first(&self) -> Self::Item;
    fn second(&self) -> Self::Item;
}

Но тогда вы бы наткнулись на неспособность описать это Item как Cow<'a, str>, где 'a - время жизни self. Попытка 3 была близка к решению путем добавления параметра времени жизни к самой характеристике, что делало этот произвольный срок жизни аргументом с более высоким рейтингом для характеристики.

pub trait Pair<'a> {
    type Item: 'a + Borrow<str>;
    fn first(&'a self) -> Self::Item;
    fn second(&'a self) -> Self::Item;
}

Этот Pair<'a> теперь определяет пару элементов, связанных с 'a, и не обязательно содержатся в self. Одна возможная реализация:

impl<'a> Pair<'a> for (String, String) {
    type Item = Cow<'a, str>;

    fn first(&'a self) -> Self::Item {
        Cow::Borrowed(&self.0)
    }
    fn second(&'a self) -> Self::Item {
        Cow::Borrowed(&self.1)
    }
}

Полный пример на Rust Playground

Этот подход достигается за счет загрязнения всех API-интерфейсов, использующих эту черту, границами черт с более высоким рейтингом, так что мы можем реализовать Pair<'a> для всех жизней 'a. Например:

fn foo<T>(pair: T)
where
    for<'a> T: Pair<'a>,
{
    unimplemented!()
}

Чтобы достичь этого времени жизни 'self для ограничения связанного типа Item, нам нужно Общих ассоциированных типов (GAT). После внедрения мы сможем написать что-то вроде этого:

pub trait Pair {
    type Item<'a>: Borrow<str>;
    fn first(&'a self) -> Self::Item<'a>;
    fn second(&'a self) -> Self::Item<'a>;
}

impl Pair for (String, String) {
    type Item<'a> = Cow<'a, str>;

    fn first(&'a self) -> Self::Item<'a> {
        Cow::Borrowed(&self.0)
    }
    fn second(&'a self) -> Self::Item<'a> {
        Cow::Borrowed(&self.1)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...