types - 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)
})
}
}
}
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
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 Playground中找到
有关更多上下文Join
,请参阅我较早的Stack Overflow 问题和我的实际源代码。
解决方案
似乎编译器无法解决iter()
返回类型所需的特征要求JoinIter<<&T as IntoIterator>::IntoIter, &S>
。
我从rustc --explain E0275
错误解释中收到了一个提示:
当递归特征要求在评估之前溢出时,会发生此错误。这通常意味着在解决某些类型边界时存在无限递归。
我不知道rust的推理过程的细节,我想会发生以下情况。
拿这个签名:
fn iter(&self) -> JoinIter<<&T as IntoIterator>::IntoIter, &S>
编译器尝试从以下位置推断返回类型:
JoinIter<<&T as IntoIterator>::IntoIter, &S>
但从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)
}
}
又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 在定义而不是使用特征时检查特征是否有效。
有关惰性标准化工作进展的更多详细信息和指针,请参阅此帖子,这可能会解决此问题。
推荐阅读
- javascript - 需要计算一个连接表行sequelize
- ios - 如何更改具有 7 个项目的 UITabBar 控制器的背景颜色?
- javascript - 使用 ajax 上传文件会引发 Uncaught TypeError: Illegal invocation
- python - 在 Tensorflow.js 中加载转换后的 Keras LSTM 模型会导致张量形状错误
- laravel - Laravel 5.2 orderBy 与计数的关系导致 SQL 错误,因为尝试获取列而不是计数失败
- windows - 即使从批处理文件启动的程序已终止,如何保持控制台窗口打开?
- ios - ObjectiveC - WebView 没有从顶部重新加载
- c - bluez 适配器 api StartDiscovery 超时
- java - 为什么 IntelliJ 需要 Lombok 插件?
- django - 为什么我会违反 ForeignKey 约束