Rust 中这个奇怪的递归类型错误是怎么回事? [英] What's going on with this bizarre recursive type error in Rust?

查看:51
本文介绍了Rust 中这个奇怪的递归类型错误是怎么回事?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为 Join 的可迭代结构:

I have an iterable struct called 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)
    }
}

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

This compiles and passes usage tests.

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

I also have an iter method defined on my 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()
    }
}

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

This compiles fine, but when I actually try to use it:

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);
}

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

I get some kind of weird type recursion error:

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 多行递归类型错误)

(I redacted about 100 more lines of recursive type error in the ...)

据我所知,它似乎试图主动评估 &Join<_, _> 是否实现了 IntoIterator,这需要检查 &Join<Join<_, _>, _> 执行 IntoIterator,以此类推.我想不通的是为什么它认为它必须这样做,因为我的实际类型完全限定为 Join.我尝试过的一些事情:

As best I can tell, it appears to be attempting to proactively evaluate whether &Join<_, _> implements IntoIterator, which requires checking if &Join<Join<_, _>, _> fulfills IntoIterator, and so on forever. What I can't figure out is why it thinks it has to do this, since my actual type is fully qualified as Join<Vec<{integer}, &'static str>. Some things I've tried:

  • 将 trait bound 从 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() 区分开来.我已经尝试了以下所有模式:

Replacing self.into_iter() with the underlying expression, JoinIter::new(self.container.into_iter(), self.separator), in the hopes that maybe it's struggling to differentiate self.into_iter() from (&self).into_iter(). I've tried all of the following patterns:

  • 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) }

为什么 (&join).into_iter() 有效,但 join.iter() 无效,即使 iter()> 只是在幕后调用 self.into_iter()?

Why does (&join).into_iter() work, but join.iter() does not, even though iter() simply calls self.into_iter() under the hood?

这个具有相同代码的完整示例也可在 铁锈游乐场

This complete example, with identical code, is also available in the Rust Playground

有关 Join 的更多背景信息,请参阅我较早的 堆栈溢出问题和我的实际 堆栈溢出问题a href="https://github.com/Lucretiel/joinery/blob/9bc3b2ddaa2609e7f9a9bc499555255eff2c334b/src/iter.rs" rel="nofollow noreferrer">源代码.

For more context about Join, see my older Stack Overflow question and my actual source code.

推荐答案

看来编译器无法解决 iter() 返回类型 JoinIter<<< 所需要的 trait 要求;&T as IntoIterator>::IntoIter, &S>.

It seems that the compiler is not able to resolve the trait requirement needed by iter() return type JoinIter<<&T as IntoIterator>::IntoIter, &S>.

我从 rustc --explain E0275 错误解释处收到了一个提示:

I've received an hint for this from rustc --explain E0275 error explanation:

当存在溢出的递归特征要求时发生此错误在它可以被评估之前.通常这意味着有无限的解决某些类型边界的递归.

This error occurs when there was a recursive trait requirement that overflowed before it could be evaluated. Often this means that there is unbounded recursion in resolving some type bounds.

我不知道 rust 推理过程的细节,我想象正在发生以下情况.

I don't know the details of the inference process of rust, I imagine the following is happening.

拿下这个签名:

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

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

The compiler try to infer the return type from:

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

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

but <&T as IntoIterator>::IntoIter is inferred from the &'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 等等,无穷无尽.

and IntoIter is again a JoinIter<<&'a T as IntoIterator>::IntoIter, &'a S> that has an IntoIter and so and so ad infinitum.

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

A way to make it compile it is to help the compiler with a 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 会检查 trait 在定义时是否有效,而不是在使用时.

generate a recursion because Rust checks that traits are valid when they are defined rather than when they are used.

有关更多详细信息,请参阅这篇文章以及工作进展的指示惰性归一化,可以解决这个问题.

See this post for some more details and pointers to the progress of work of lazy normalization, which may solve this problem.

这篇关于Rust 中这个奇怪的递归类型错误是怎么回事?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆