"Ожидаемый связанный тип, найденный" u32 ", когда используется время жизни параметра как параметр параметра, в котором

Я попытался скомпилировать этот код (игровая площадка):

trait Family<'a> {
    type Out;
}

struct U32Family;
impl<'a> Family<'a> for U32Family {
    type Out = u32;
}


trait Iterator {
    type Item;
    fn next<'s>(& mut self) -> <Self::Item as Family<'s>>::Out
    where
        Self::Item: Family<'s>;
}


struct Foo;
impl Iterator for Foo {
    type Item = U32Family;

    fn next<'s>(& mut self) -> <Self::Item as Family<'s>>::Out
    where
        Self::Item: Family<'s>,
    {
        0u32  // <-- in real code, this is somehow calculated
    }
}

Но, к сожалению, это приводит к этой ошибке:

error[E0308]: mismatched types
  --> src/main.rs:28:9
   |
24 |     fn next<'s>(& mut self) -> <Self::Item as Family<'s>>::Out
   |                                  ------------------------------- expected '<U32Family as Family<'s>>::Out' because of return type
...
28 |         0u32
   |         ^^^^ expected associated type, found u32
   |
   = note: expected type '<U32Family as Family<'s>>::Out'
              found type 'u32'

Я действительно не понимаю, почему. Очевидно, что в этом фрагменте кода <U32Family as Family<'s>>::Out является именно u32. Но Рур кажется, что это не всегда одно и то же. Зачем? И как я могу скомпилировать его?

Некоторые примечания:

  • Есть куча подобных ситуаций, когда происходит аналогичная ошибка, но я думаю, что это отличается от всего, что я видел до сих пор.
  • Я не могу использовать type Out: for<'a> Family<'a>; , Так что это не обходной путь, который работает для меня.
  • Если я удалю параметр жизни Family, все будет работать.
  • Если я заменил Family<'s> на Family<'static> в сигнатуре функции, все будет работать.

EDIT: я могу обойти эту проблему, добавив:

impl U32Family {
    fn from<'a>(v: u32) -> <Self as Family<'a>>::Out {
        v
    }
}

Тогда я могу просто сказать Self::Item::from(0u32) в теле next(). (Детская площадка)

Я думаю, что понятно, почему ошибка в next() отсутствует: U32Family::from всегда принимает u32 качестве аргумента. Запрограммированный. Никогда не меняйте. Большой вопрос об этом обходном пути: почему метод from() компилируется отлично? Поэтому in from() компилятор каким-то образом знает, что <Self as Family<'a>>::Out всегда u32, но если я попробую то же самое в next(), то каким-то образом компилятор не понимает, что <Self::Item as Family<'s>>::Out is u32. Теперь я еще более смущен.

EDIT2: во-первых, я подозревал, что проблема специализации. Например, вы можете написать:

impl Family<'static> for U32Family {
    type Out = char;
}

Тогда, конечно, компилятор был бы прав, считая, что u32 не всегда совпадает с <Self::Item as Family<'s>>::Out для любого 's. Однако, я думаю, это не проблема.

Прежде всего, импланты, которые могут быть специализированными, должны быть отмечены ключевым словом по default. Я этого не делал, поэтому я мог бы предположить, что связанный тип на самом деле u32 (в RFC говорится о чем-то очень похожем). Но, кроме того, специализация, основанная на сроках жизни , не допускается.

Поэтому я склонен думать, что это ошибка компилятора. Но я хотел бы получить еще один ответ!

Ответ 1

struct U32Family;
...
impl Iterator for Foo {
type Item = U32Family;

Поэтому next() должен возвращать Option<U32Family>, единственными возможными значениями которой являются None и Some(U32Family{})

Вероятно, вам понадобится Item = <U32Family as Family<'static>::Out который исправляет эту проблему, но создает некоторые проблемы с продолжительностью жизни. (Элемент нуждается в жизни, потому что у семьи есть одно, но вы принимаете только жизнь на next())

Ответ 2

Я думаю, что проблема в том, что это "совпадение", что <Self::Item as Family<'s>>::Out является u32 для всех 's. Компилятор может доказать это для любого 's вы хотите, но он не может даже выразить концепцию, что это верно для всех 's.

U32Family который вы нашли, - это правильный подход: добавьте метод U32Family который преобразует u32 в <Self as Family<'a>>::Out. Тело метода полностью находится в области 'a, поэтому компилятор может доказать, что преобразование корректно для текста 'a, и поэтому метод верен. Затем, на сайте вызова, вы говорите компилятору, чтобы использовать его знания о методе.