线程'<main>'溢出了Rust </main>中的堆栈

时间:2015-03-07 10:45:24

标签: rust

我在尝试此代码时出错,它实现了一个简单的链接列表。

use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    a : Option<Rc<RefCell<Node>>>,
    value: i32
}

impl Node {
    fn new(value: i32) -> Rc<RefCell<Node>> {
        let node = Node {
            a: None,
            value: value
        };
        Rc::new(RefCell::new(node))
    }
}

fn main() {
    let first  = Node::new(0);
    let mut t = first.clone();
    for i in 1 .. 10_000
    {
        if t.borrow().a.is_none() { 
            t.borrow_mut().a = Some(Node::new(i));
        }
        if t.borrow().a.is_some() {
            t = t.borrow().a.as_ref().unwrap().clone();
        }
    }
    println!("Done!");
}

为什么会这样?这是否意味着Rust不像定位那么安全?

UPD: 如果我添加此方法,程序不会崩溃。

impl Drop for Node {
    fn drop(&mut self) {
        let mut children = mem::replace(&mut self.a, None);

        loop {
            children = match children {
                Some(mut n) => mem::replace(&mut n.borrow_mut().a, None),
                None => break,
            }
        }
    }
}

但我不确定这是否是正确的解决方案。

2 个答案:

答案 0 :(得分:4)

  

这是否意味着Rust不如定位那么安全?

Rust只能安全地防止某些类型的失败;特别是内存损坏崩溃,这里记录了:http://doc.rust-lang.org/reference.html#behavior-considered-undefined

不幸的是,有时候人们会期望生锈对某些不是内存破坏的故障更加强大。具体来说,您应该阅读http://doc.rust-lang.org/reference.html#behavior-considered-undefined

tldr;在生锈中,许多事情都会引起恐慌。紧急情况将导致当前线程停止,执行关机操作。

这可能表面看起来类似于内存破坏来自其他语言的崩溃,但重要的是要了解它虽然是应用程序失败,但它不是破坏内存的内存。

例如,您可以通过在不同的线程中运行操作来处理恐慌,例如在线程发生混乱时(无论出于何种原因)正常处理失败。

在这个具体的例子中,你在堆栈上耗尽了太多内存。

这个简单的例子也会失败:

fn main() {
  let foo:&mut [i8] = &mut [1i8; 1024 * 1024];
}

(大多数生锈;取决于特别实施的堆栈大小)

我原本以为使用Box :: new()将分配移动到堆栈会在此示例中修复它...

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    a : Option<Box<Rc<RefCell<Node>>>>,
    value: i32
}

impl Node {
    fn new(value: i32) -> Box<Rc<RefCell<Node>>> {
        let node = Node {
            a: None,
            value: value
        };
        Box::new(Rc::new(RefCell::new(node)))
    }
}

fn main() {
    let first  = Node::new(0);
    let mut t = first.clone();
    for i in 1 .. 10000
    {
        if t.borrow().a.is_none() {
            t.borrow_mut().a = Some(Node::new(i));
        }
        if t.borrow().a.is_some() {
            let c:Box<Rc<RefCell<Node>>>;
            { c = t.borrow().a.as_ref().unwrap().clone(); }
            t = c;
            println!("{:?}", t);
        }
    }
    println!("Done!");
}

......但事实并非如此。我真的不明白为什么,但希望其他人可以看看这个,并发布一个更权威的答案,说明你的代码中究竟是什么导致了堆栈耗尽。

答案 1 :(得分:1)

对于那些来这里并且对大型结构是连续的内存块(而不是盒子树)的情况特别感兴趣的人,我发现了这个GitHub问题,需要进一步讨论,以及一个可行的解决方案为了我: https://github.com/rust-lang/rust/issues/53827

  

Vec的方法package com.test.algorithms; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; public class PrintInOrder { private static Integer[] a = { 1, 1, 1 }; private static Integer[] b = { 2, 2, 2 }; private static Integer[] c = { 3, 3, 3 }; private static Integer[] d = { 4, 4, 4 }; public static void main(String[] args) throws InterruptedException { QueueOrder q1 = null; QueueOrder q2 = null; QueueOrder q3 = null; QueueOrder q4 = null; q1 = new QueueOrder(a); q2 = new QueueOrder(b); q3 = new QueueOrder(c); q4 = new QueueOrder(d); q1.setChild(q2); q2.setChild(q3); q3.setChild(q4); q4.setChild(q1); Thread t1 = new Thread(q1); Thread t2 = new Thread(q2); Thread t3 = new Thread(q3); Thread t4 = new Thread(q4); t1.start(); t2.start(); t3.start(); t4.start(); QueueOrder q = q1; while (!q.queue.isEmpty()) { synchronized (q) { if (!q.isPrinted) { q.notify(); q.wait(); } } q = q.child; } t1.join(); t2.join(); t3.join(); t4.join(); } } class QueueOrder implements Runnable { Integer[] arr; QueueOrder child; Queue<Integer> queue = new LinkedList<>(); boolean isPrinted = false; QueueOrder(Integer[] arr) { this.arr = arr; queue.addAll(Arrays.asList(arr)); } public QueueOrder getChild() { return child; } public void setChild(QueueOrder child) { this.child = child; } public void run() { while (!this.queue.isEmpty()) { synchronized (this) { if (!this.isPrinted) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } System.out.print("**" + this.queue.poll()); this.isPrinted = false; synchronized (this) { this.notify(); } } } } 返回一个into_boxed_slice(),并且不会为我溢出堆栈。

     

Box<[T]>

     

与vec的不同之处!来自文档的宏和数组表达式:

     
    

这将使用克隆复制一个表达式,因此,在具有非标准Clone实现的类型上使用此表达式时应格外小心。

  
     

在Vec上还有一种vec![-1; 3000000].into_boxed_slice()方法,如with_capacity()示例所示。