构造大树时“线程'<main>'已溢出其堆栈”

Posted

技术标签:

【中文标题】构造大树时“线程\'<main>\'已溢出其堆栈”【英文标题】:"thread '<main>' has overflowed its stack" when constructing a large tree构造大树时“线程'<main>'已溢出其堆栈” 【发布时间】:2015-04-23 23:37:34 【问题描述】:

我实现了一个树结构:

use std::collections::VecDeque;
use std::rc::Rc, Weak;
use std::cell::RefCell;

struct A 
    children: Option<VecDeque<Rc<RefCell<A>>>>


// I got thread '<main>' has overflowed its stack
fn main()
    let mut tree_stack: VecDeque<Rc<RefCell<A>>> = VecDeque::new();

    // when num is 1000, everything works
    for i in 0..100000 
        tree_stack.push_back(Rc::new(RefCell::new(A children: None)));
    

    println!(":?", "reach here means we are not out of mem");
    loop 
        if tree_stack.len() == 1 break;

        let mut new_tree_node = Rc::new(RefCell::new(A children: None));
        let mut tree_node_children: VecDeque<Rc<RefCell<A>>> = VecDeque::new();

        // combine last two nodes to one new node
        match tree_stack.pop_back() 
            Some(x) => 
                tree_node_children.push_front(x);
            ,
            None => 
         
        match tree_stack.pop_back() 
            Some(x) => 
                tree_node_children.push_front(x);
            ,
            None => 
         

        new_tree_node.borrow_mut().children = Some(tree_node_children);
        tree_stack.push_back(new_tree_node);
    

Playpen link

但它崩溃了

thread '<main>' has overflowed its stack

我该如何解决这个问题?

【问题讨论】:

欢迎来到 Stack Overflow!要获得最佳质量的答案,请查看How do I ask a good question?。在这种特殊情况下,您应该包含内联代码,而不仅仅是指向它的链接(链接可能会失效,然后未来的程序员将无法受益!)。不要只是发布所有代码!相反,构造一个MCVE 来缩小问题范围并让回答者更容易。您应该只提出一个问题,并对意见问题(“最佳实践”)保持谨慎,因为它们超出了本网站的范围。 【参考方案1】:

您遇到的问题是因为您有一个巨大的节点链表。当该列表被删除时,第一个元素首先尝试释放结构的所有成员。这意味着第二个元素做同样的事情,依此类推,直到列表的末尾。这意味着您将拥有一个与列表中的元素数量成正比的调用堆栈!

这是一个小复制:

struct A 
    children: Option<Box<A>>


fn main() 
    let mut list = A  children: None ;

    for _ in 0..1_000_000 
        list = A  children: Some(Box::new(list)) ;
    

以下是你将如何解决它:

impl Drop for A 
    fn drop(&mut self) 
        if let Some(mut child) = self.children.take() 
            while let Some(next) = child.children.take() 
                child = next;
            
        
    

此代码用迭代的方式覆盖默认的递归删除实现。它将children 从节点中剥离出来,并用终端项目(None)替换它。然后它允许节点正常丢弃,但不会有递归调用。

代码有点复杂,因为我们不能放弃自己,所以我们需要跳两步舞,忽略第一个项目,然后吃掉所有的孩子。

另见:

How can I swap in a new value for a field in a mutable reference to a structure? How do I move out of a struct field that is an Option?

【讨论】:

我遇到了同样的问题,只是代码略有不同。在您的示例复制中,我不明白何时删除列表。您不只是添加一系列链接节点吗?为什么会溢出堆栈? @Miguel list 变量在其作用域的末尾(main 的末尾)被删除。堆栈溢出是因为A 的默认Drop 实现调用Option&lt;Box&lt;A&gt;&gt;Drop 实现,它调用Box&lt;A&gt;Drop 实现,它调用ADrop 实现(等等)。每个函数调用都会增加堆栈深度。

以上是关于构造大树时“线程'<main>'已溢出其堆栈”的主要内容,如果未能解决你的问题,请参考以下文章

侧滑窗口最大值相关题目

Spring这棵大树

一颗种子能长成参天大树,是细胞分裂的结果。

998. 最大二叉树 II

大树编程祝大家鼠年大吉!

住一层窗外一棵大树小区里的孩子总上树又吵还总窥视屋里怎么办?