实例化std :: vec :: IntoIter <i32>时达到类型长度限制

时间:2018-12-31 13:52:28

标签: rust

我正在尝试实现Tree结构,但是每当尝试运行以下代码时,我都会不断收到错误消息:

fn main() {
    let tree = Tree::create(1, |_| Vec::new());
    println!("{:?}", tree);
}

#[derive(Debug)]
struct Tree<T> {
    value: T,
    children: Vec<Tree<T>>,
}

impl<T> Tree<T> {
    fn create<F>(value: T, get_children: F) -> Tree<T>
    where
        F: Fn(&T) -> Vec<T>,
    {
        let children = get_children(&value);
        Tree {
            value,
            children: children
                .into_iter()
                .map(|x| Tree::create(x, |y| get_children(y)))
                .collect(),
        }
    }
}

错误:

error: reached the type-length limit while instantiating `<std::vec::IntoIter<i32> as std::iter::Iterator>::map::<Tree<i32...`
  |
  = note: consider adding a `#![type_length_limit="2097152"]` attribute to your crate

2 个答案:

答案 0 :(得分:1)

您在创建Tree<T>时正在进行递归调用:

impl<T> Tree<T> {
    fn create<F>(value: T, get_children: F) -> Tree<T>
    //...
    //...
        .map(|x| Tree::create(x, |y| get_children(y))) //endless recursive call
  

我很困惑为什么自从我返回一个空后就无限递归   封闭中的向量。

此错误在编译过程中发生,并且错误表明为reached the type-length limit while instantiating...。这意味着您正在生成一个非常长的类型。

它是怎么发生的?

调用Tree::create(x, |y| get_children(y))时,您正在创建一个参数闭包,该闭包将调用现有闭包。没关系,但是当您递归调用它时,编译器将无法在最内部的调用中检测到F的类型。

记住get_children的类型为F,其中F: Fn(&T) -> Vec<T>。 首次调用Tree::create时,将像这样推断出F中的create<F>

let tree = Tree::create(1, |_| Vec::new());
//inference of F: Fn(&T) -> Vec<T>

map(...)中第二次通话后:

Tree::create(x, |y| get_children(y))
//inference of F: Fn(&T) -> Fn(&T) -> Vec<T>

然后它将最终变成:

//inference of F: Fn(&T)-> Fn(&T) -> Fn(&T) -> Vec<T>
//inference of F: Fn(&T)-> ... -> Fn(&T) -> Fn(&T) -> Vec<T>

最后,编译器达到了类型长度限制。

递归解决方案

除了Shepmaster's answer之外,您还可以使用function pointers

impl<T> Tree<T> {
    fn create(value: T, get_children: fn(&T) -> Vec<T>) -> Tree<T> {
        let children = get_children(&value);
        Tree {
            value,
            children: children
                .into_iter()
                .map(|x| Tree::create(x, get_children))
                .collect(),
        }
    }
}

无需递归的解决方案

您可以通过将函数作为Vec<Tree<T>>发送到get_children而不是像这样在create中生成函数来解决此问题:

fn main() {
    let inner_tree = Tree::create(1, |_| Vec::new());
    let tree = Tree::create(1, move |_| vec![inner_tree]);
    println!("{:?}", tree);
}

#[derive(Debug)]
struct Tree<T> {
    value: T,
    children: Vec<Tree<T>>,
}

impl<T> Tree<T> {
    fn create<F>(value: T, get_children: F) -> Tree<T>
    where
        F: FnOnce(&T) -> Vec<Tree<T>>,
    {
        let children = get_children(&value);
        Tree { value, children }
    }
}

请注意,我将函数参数的类型从Fn更改为FnOnce。需要将内部树的所有权移至闭包中。它将被调用一次,以便可以使用该变量。

答案 1 :(得分:1)

这是与What does "Overflow evaluating the requirement" mean and how can I fix it?相同的潜在问题,可以用相同的方法解决。这意味着您可以通过使用引用特征对象来避免类型级别的递归:

impl<T> Tree<T> {
    fn create(value: T, mut get_children: impl FnMut(&T) -> Vec<T>) -> Tree<T> {
        fn create_inner<T>(value: T, get_children: &mut FnMut(&T) -> Vec<T>) -> Tree<T> {
            let children = get_children(&value)
                .into_iter()
                .map(|x| create_inner(x, get_children))
                .collect();

            Tree { value, children }
        }

        create_inner(value, &mut get_children)
    }
}

我也从Fn切换到FnMut,因为在可能的情况下使用闭包类型更好些。