【问题标题】:Reached the type-length limit while instantiating std::vec::IntoIter<i32>实例化 std::vec::IntoIter<i32> 时达到类型长度限制
【发布时间】:2018-12-31 13:52:27
【问题描述】:

我正在尝试实现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

【问题讨论】:

    标签: rust


    【解决方案1】:

    你在创建Tree&lt;T&gt;时进行递归调用:

    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(&amp;T) -&gt; Vec&lt;T&gt;。 当你第一次调用Tree::create时,create&lt;F&gt;中的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(),
            }
        }
    }
    

    没有递归的解决方案

    您可以通过将函数作为get_children 发送到Vec&lt;Tree&lt;T&gt;&gt; 而不是在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。需要将内部树的所有权转移到闭包中。它将被调用一次,以便它可以使用该变量。

    【讨论】:

    • 感谢您的帮助。递归是有意的——我希望采用树状结构(例如 DOM 树)并将其包装在正式的树结构中,这样我就可以添加方法来映射、折叠等。我很困惑为什么它会无限递归我在闭包中返回了一个空向量。
    • 感谢您的精彩解释。错误消息现在更有意义了。那么我想要实现的目标是不可能的吗?
    • @AdamPutinski 我无法回复,再次编辑答案,希望对您有所帮助。
    • @Shepmaster 这种情况需要吗?
    • @Shepmaster 确实,但 imo 预期的称为 get_children 的参数应该只得到孩子。尽管我只是想为您的代码添加简洁性(您使用 Immutable Fn),但对于这种情况,可变行为是一个糟糕的设计。
    【解决方案2】:

    这是与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,因为如果可能的话,更灵活的闭包类型会更好。

    【讨论】:

    • 谢谢。这很有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-23
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多