У меня есть итеративная структура с именем 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
см. Мой старый вопрос переполнения стека и мой фактический исходный код .