创建链接列表时意外的堆栈溢出

时间:2014-08-06 12:01:22

标签: rust

通过扩展Rust tutorial

提供的链接列表示例,我一直试图找到我的方法

我编写了一个将泛型向量转换为链表的构造函数,但如果向量足够大,则使用它会导致堆栈溢出。这让我感到困惑,因为我认为链表是在堆上创建的,因为列表中的每个元素都有一个包含下一个元素的拥有框。

Rust是我用过的第一种语言,我不得不担心内存管理,所以我有点迷失。一个解释将是值得赞赏的,这是一个双重解决方案。谢谢!

我的代码仅截断到相关的gubbins:

use std::mem::swap;

enum ListItem<T> { 
    Node(T, Box<ListItem<T>>), 
    Cap 
}

impl <T> ListItem<T> {
    fn append(&mut self, x: T) {
        match *self {
            Node(_, ref mut a@box Cap) => *a = box Node(x, box Cap),
            Node(_, ref mut a@box Node(_, _)) => a.append(x),
            Cap => {
                let mut n = Node(x, box Cap);
                swap(&mut n, self);
            }
        };
    }
}

impl <T: Clone> ListItem<T> {
    fn new_from_vec(x: &mut Vec<T>) -> ListItem<T>{
        let mut l = Cap;
        for v in x.iter() {
            l.append((*v).clone());
        }
        return l;
    }
}

fn main() {
    let mut v: Vec<int> = vec![];

    for n in range(1i, 500001) {
        v.push(n);
    }

    println!("Done creating vector.");

    let x = ListItem::new_from_vec(&mut v);

    println!("Done creating linked list.");
}

打印Done creating vector.,然后在到达下一个task '<main>' has overflowed its stack之前打印println!

2 个答案:

答案 0 :(得分:2)

堆栈溢出通常表示递归过于深入。如果仔细查看代码,可能会发现递归:

impl <T> ListItem<T> {
    fn append(&mut self, x: T) {
        match *self {
            Node(_, ref mut a@box Cap) => *a = box Node(x, box Cap),
            Node(_, ref mut a@box Node(_, _)) => a.append(x),       // <---
            Cap => {
                let mut n = Node(x, box Cap);
                swap(&mut n, self);
            }
        };
    }
}

这意味着对于列表中已有的每个元素,您将在堆栈上创建一个新的函数框架(除非编译器对其进行优化),对于足够数量的元素,这将确定性地溢出堆栈。

为避免溢出,您可以切换到迭代版本(带循环)。

答案 1 :(得分:2)

不幸的是,Rust没有tail call optimizations,这意味着你不能依赖递归来进行迭代繁重的操作。

根据this ticket,他们似乎不太可能在将来添加此功能。

我担心你必须将代码转换为常规循环。