使用impl trait返回递归迭代器时溢出评估需求

时间:2018-12-31 15:53:24

标签: recursion tree rust iterator overflow

我正在尝试在Rust中对树结构进行深度优先的迭代。我以为我有一个非常好的简洁解决方案,但是我无法编译它。从概念上讲,这很简单:遍历子级,首先获得每个子级的深度迭代器,然后将它们展平,然后将当前节点的元数据迭代器链接到子级。

#[derive(Debug, Eq, PartialEq)]
struct Node {
    metadata: Vec<i64>,
    children: Vec<Box<Node>>,
}

impl Node {
    fn depth_first_metadata_iter(&self) -> impl Iterator<Item = &i64> + '_ {
        self.children
            .iter()
            .map(|child| child.depth_first_metadata_iter())
            .flatten()
            .chain(self.metadata.iter())
    }
}

fn main() {
    let tree = Node {
        metadata: vec![1, 2, 3],
        children: vec![
            Box::new(Node {
                metadata: vec![4, 5],
                children: vec![],
            }),
            Box::new(Node {
                metadata: vec![6, 7],
                children: vec![],
            }),
        ],
    };
    println!(
        "{:?}",
        tree.depth_first_metadata_iter().collect::<Vec<&i64>>()
    );
}

但是,当我编译它时,出现以下错误:

error[E0275]: overflow evaluating the requirement `impl std::iter::Iterator`
  |
  = help: consider adding a `#![recursion_limit="128"]` attribute to your crate

(您可以自己在playground上进行检查。)

这是有道理的,因为我正在depth_first_metadata_iter内进行递归调用,这些调用返回嵌套的迭代器,但是如果类似这样的代码可以在不需要实现自定义迭代器的情况下工作,那将非常好。

我所见过的其他所有解决E0275错误的方法(例如thisthisthis)似乎都涉及到在策略上将类型注释放置在某处–这样的可能性,还是我尝试“不可能”的事情?

1 个答案:

答案 0 :(得分:1)

  

如果类似这样的代码可以工作

取决于您的意思。这是相似的,可以工作,并且不需要自定义迭代器。从而满足您的所有要求:

fn depth_first_metadata_iter(&self) -> Box<Iterator<Item = &i64> + '_> {
    Box::new({
        self.children
            .iter()
            .flat_map(|child| child.depth_first_metadata_iter())
            .chain(self.metadata.iter())
    })
}

从本质上讲,这是与

中所示的问题相同的问题。

暂时搁置一下编译器。您的原始代码说:“我将返回一个具体的迭代器类型,但我不会说确切的类型”。编译器仍然必须能够找出该类型,所以让我们成为编译器:

let a = self.children.iter();
// std::slice::Iter<'_, Box<Node>>

let cls = |child| child.depth_first_metadata_iter();
// Fn(&Box<Node>) -> ?X?

let b = a.flat_map(cls);
// FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>

let d = self.metadata.iter();
// std::slice::Iter<'_, i64>

b.chain(d);
// Chain<FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>, Iter<'_, i64>>

此最终结果是返回值,因此我们得到了等式:

Chain<FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>, Iter<'_, i64>> === ?X?

AFAIK,无法执行类型级代数来求解?X?,因此会出现错误。

将返回类型更改为盒装特征对象会短路所有需要的逻辑,并强制使用特定的具体类型。

  

从策略上将类型注释放置在某处

我认为情况并非如此。如果是这样,那就意味着代数 是可解的,但是编译器不够聪明,无法解决它。虽然在其他情况下无疑是正确的,但我认为不是在这里。


我认为这不是一个 great 解决方案,因为这会涉及很多微小的分配。我假设(但尚未测试)使用堆栈数据结构的自定义迭代器会更有效。

中间点是建立整个节点集:

impl Node {
    fn depth_first_metadata_iter(&self) -> impl Iterator<Item = &i64> + '_ {
        self.x().into_iter()
    }

    fn x(&self) -> Vec<&i64> {
        fn x_inner<'a>(node: &'a Node, v: &mut Vec<&'a i64>) {
            for c in &node.children {
                x_inner(c, v)
            }
            v.extend(&node.metadata);
        }

        let mut v = Vec::new();
        x_inner(self, &mut v);
        v
    }
}