Что происходит с этой странной ошибкой рекурсивного типа в Rust? - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть итеративная структура с именем Join:

use std::iter::Peekable;

#[derive(Debug)]
pub struct Join<T, S> {
    container: T,
    separator: S,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinItem<T, S> {
    Element(T),
    Separator(S),
}

pub struct JoinIter<Iter: Iterator, Sep> {
    iter: Peekable<Iter>,
    sep: Sep,
    next_sep: bool,
}

impl<Iter: Iterator, Sep> JoinIter<Iter, Sep> {
    fn new(iter: Iter, sep: Sep) -> Self {
        JoinIter {
            iter: iter.peekable(),
            sep,
            next_sep: false,
        }
    }
}

impl<I: Iterator, S: Clone> Iterator for JoinIter<I, S> {
    type Item = JoinItem<I::Item, S>;

    /// Advance to the next item in the Join. This will either be the next
    /// element in the underlying iterator, or a clone of the separator.
    fn next(&mut self) -> Option<Self::Item> {
        let sep = &self.sep;
        let next_sep = &mut self.next_sep;

        if *next_sep {
            self.iter.peek().map(|_| {
                *next_sep = false;
                JoinItem::Separator(sep.clone())
            })
        } else {
            self.iter.next().map(|element| {
                *next_sep = true;
                JoinItem::Element(element)
            })
        }
    }
}

Ссылка на Join реализует IntoIterator:

impl<'a, T, S> IntoIterator for &'a Join<T, S>
where
    &'a T: IntoIterator,
{
    type IntoIter = JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>;
    type Item = JoinItem<<&'a T as IntoIterator>::Item, &'a S>;

    fn into_iter(self) -> Self::IntoIter {
        JoinIter::new(self.container.into_iter(), &self.separator)
    }
}

Это компилирует и проходит тесты использования.

У меня также есть метод iter, определенный в моей Join struct:

impl<T, S> Join<T, S>
where
    for<'a> &'a T: IntoIterator,
{
    pub fn iter(&self) -> JoinIter<<&T as IntoIterator>::IntoIter, &S> {
        self.into_iter()
    }
}

Это прекрасно компилируется, но когда я на самом деле пытаюсь его использовать:

fn main() {
    // Create a join struct
    let join = Join {
        container: vec![1, 2, 3],
        separator: ", ",
    };

    // This works fine
    let mut good_ref_iter = (&join).into_iter();
    assert_eq!(good_ref_iter.next(), Some(JoinItem::Element(&1)));
    assert_eq!(good_ref_iter.next(), Some(JoinItem::Separator(&", ")));
    assert_eq!(good_ref_iter.next(), Some(JoinItem::Element(&2)));
    assert_eq!(good_ref_iter.next(), Some(JoinItem::Separator(&", ")));
    assert_eq!(good_ref_iter.next(), Some(JoinItem::Element(&3)));
    assert_eq!(good_ref_iter.next(), None);

    // This line fails to compile; comment out this section and it all works
    let bad_ref_iter = join.iter();
    assert_eq!(bad_ref_iter.next(), Some(JoinItem::Element(&1)));
    assert_eq!(bad_ref_iter.next(), Some(JoinItem::Separator(&", ")));
    assert_eq!(bad_ref_iter.next(), Some(JoinItem::Element(&2)));
    assert_eq!(bad_ref_iter.next(), Some(JoinItem::Separator(&", ")));
    assert_eq!(bad_ref_iter.next(), Some(JoinItem::Element(&3)));
    assert_eq!(bad_ref_iter.next(), None);
}

Я получаю некоторую странную ошибку рекурсии типа:

error[E0275]: overflow evaluating the requirement `&_: std::marker::Sized`
   --> src/join.rs:288:29
    |
 96 |         let mut iter = join.iter();
    |                             ^^^^
    |
    = help: consider adding a `#![recursion_limit="128"]` attribute to your crate
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&_`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<_, _>`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<join::Join<_, _>, _>`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<join::Join<join::Join<_, _>, _>, _>`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<join::Join<join::Join<join::Join<_, _>, _>, _>, _>`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<join::Join<join::Join<join::Join<join::Join<_, _>, _>, _>, _>, _>`
    = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&join::Join<join::Join<join::Join<join::Join<join::Join<join::Join<_, _>, _>, _>, _>, _>, _>`
...

(я отредактировал еще около 100 строк ошибки рекурсивного типа в ...)

Как я могу сказать,кажется, что он пытается предварительно оценить, реализует ли &Join<_, _> IntoIterator, что требует проверки, выполняет ли &Join<Join<_, _>, _> IntoIterator, и так далее, навсегда.Я не могу понять, почему он считает, что должен сделать это, поскольку мой фактический тип полностью квалифицирован как Join<Vec<{integer}, &'static str>.Вот некоторые вещи, которые я пробовал:

  • Перемещение черты, привязанной к заголовку impl, в функцию iter, например:

    fn iter(&'a self) -> JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>
    where &'a T: IntoIterator
    

    Это имееттот же результат.

  • Замена self.into_iter() базовым выражением JoinIter::new(self.container.into_iter(), self.separator) в надежде на то, что, возможно, ему будет трудно отличить self.into_iter() от (&self).into_iter().Я пробовал все следующие шаблоны:

    • fn iter(&self) -> ... { self.into_iter() }
    • fn iter(&self) -> ... { (&self).into_iter() }
    • fn iter(&self) -> ... { JoinIter::new(self.container.into_iter(), &self.separator) }
    • fn iter(&self) -> ... { JoinIter::new((&self.container).into_iter(), &self.separator) }
  • Кстати, замена звонка на self.iter() на (&self).into_iter() устраняет проблему, но замена на (&self).iter() - нет.

Почему(&join).into_iter() работает, но join.iter() нет, хотя iter() просто вызывает self.into_iter() под капотом?

Этот полный пример с идентичным кодом также доступен в RustДетская площадка


Для получения дополнительной информации о Join см. Мой старый вопрос переполнения стека и мой фактический исходный код .

1 Ответ

0 голосов
/ 22 ноября 2018

Похоже, что компилятор не может разрешить требование черты, необходимое для iter() тип возвращаемого значения JoinIter<<&T as IntoIterator>::IntoIter, &S>.

Я получил подсказку для этого из rustc --explain E0275 объяснения ошибки:

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

Я не знаю деталей процесса вывода ржавчины, я думаю, что происходит следующее.

Возьмите эту подпись:

fn iter(&self) -> JoinIter<<&T as IntoIterator>::IntoIter, &S>

Компилятор пытается определить тип возвращаемого значения из:

JoinIter<<&T as IntoIterator>::IntoIter, &S>

, но <&T as IntoIterator>::IntoIter выводится из &'a Join impl:

impl<'a, T, S> IntoIterator for &'a Join<T, S>
    where &'a T: IntoIterator
{
    type IntoIter = JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>;
    type Item = JoinItem<<&'a T as IntoIterator>::Item, &'a S>;

    fn into_iter(self) -> Self::IntoIter {
        JoinIter::new(self.container.into_iter(), &self.separator)
    }
}

и IntoIter снова являются JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>, которые имеют IntoIter и т. Д. И т. Д. До бесконечности.

Способ заставить его скомпилировать это помочь компилятору с turbofish :

let mut bad_ref_iter = Join::<Vec<i32>, &str>::iter(&join);

вместо:

let bad_ref_iter = join.iter();

Обновление

Строка:

type IntoIter = JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>;    

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

См. thisпост для более подробной информации и указаний на ход работы ленивой нормализации, которые могут решить эту проблему.

...