通过扩展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!
。
答案 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,他们似乎不太可能在将来添加此功能。
我担心你必须将代码转换为常规循环。