Rust中这个奇怪的递归类型错误发生了什么?

Luc*_*iel 6 types type-inference reference rust

我有一个名为的可迭代结构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)
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

这将编译并通过使用测试.

我的结构上也iter定义了一个方法Join:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

编译很好,但是当我真正尝试使用它时:

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);
}
Run Code Online (Sandbox Code Playgroud)

我得到某种奇怪的类型递归错误:

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<_, _>, _>, _>, _>, _>, _>`
...
Run Code Online (Sandbox Code Playgroud)

(我在...中编辑了大约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
    
    Run Code Online (Sandbox Code Playgroud)

    这具有相同的结果.

  • 更换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 Playground中使用


有关更多上下文Join,请参阅我的旧Stack Overflow问题和我的实际源代码.

att*_*ona 2

编译器似乎无法解决iter()返回类型所需的特征要求JoinIter<<&T as IntoIterator>::IntoIter, &S>

我从错误解释中收到了对此的提示rustc --explain E0275

当递归特征要求在评估之前溢出时,就会发生此错误。通常,这意味着在解决某些类型界限时存在无界递归。

我不知道rust的推理过程的细节,我想象下面正在发生的事情。

拿这个签名:

fn iter(&self) -> JoinIter<<&T as IntoIterator>::IntoIter, &S>
Run Code Online (Sandbox Code Playgroud)

编译器尝试从以下位置推断返回类型:

JoinIter<<&T as IntoIterator>::IntoIter, &S>
Run Code Online (Sandbox Code Playgroud)

但从impl<&T as IntoIterator>::IntoIter推断出&'a Join

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

并且IntoIter又是一个JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>具有IntoIter无穷无尽的如此如此的a。

使其编译的一种方法是使用turbofish帮助编译器:

let mut bad_ref_iter = Join::<Vec<i32>, &str>::iter(&join);
Run Code Online (Sandbox Code Playgroud)

代替:

let bad_ref_iter = join.iter();
Run Code Online (Sandbox Code Playgroud)

更新

该行:

type IntoIter = JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S>;    
Run Code Online (Sandbox Code Playgroud)

生成递归,因为 Rust 检查特征在定义时而不是使用时是否有效。

请参阅这篇文章了解更多详细信息以及惰性规范化工作进展的指示,这可能会解决这个问题。