我正在阅读Learning Rust With Entirely Too Many Linked Lists并且我对链接列表(堆栈)needs a destructor的原因感到困惑。
我认为当列表值超出范围时,列表本身和所有节点都将被清理。它只是为了示范吗?
我尝试使用和不使用手动析构函数对版本进行基准测试,然后我找到了#34;没有析构函数"一个人有更好的表现:
for _ in 1..30000000 {
let mut list = List::new();
list.push(1);
assert_eq!(list.pop(), Some(1));
}
使用手动析构函数:
real 0m11.216s
user 0m11.192s
sys 0m 0.020s
没有手动析构函数:
real 0m9.071s
user 0m9.044s
sys 0m0.004s
答案 0 :(得分:6)
你是对的。该清单将清理自己。 正如作者所说:
所有这些都是自动处理的......只有一个故障。
然后他解释了为什么自动处理不好:
自动销毁过程为列表的头部调用drop
,然后为第一个元素调用drop
。等等等等。
这是一个函数调用一个函数调用一个函数(无限可能的重复),它会迟早炸掉你的堆栈。
例如,此测试会导致此类堆栈溢出:
#[test]
fn build_really_big_stack(){
let mut stack = List::new();
for i in 0..1_000_000{
stack.push(i);
}
}
此外,如果您使用--release
标记对可执行版本进行基准测试,则表明两个版本的执行几乎相同:
#[bench]
fn bench_auto_destructor(b: &mut Bencher) {
b.iter(|| {
{
let mut list = List::new();
for i in 0..1000 {
list.push(i);
}
assert_eq!(list.pop(), Some(999));
}
});
}
#[bench]
fn bench_man_destructor(b: &mut Bencher){
b.iter(|| {
{
let mut list = ManualDestructorList::new();
for i in 0..1000 {
list.push(i);
}
assert_eq!(list.pop(), Some(999));
}
});
}
test bench_auto_destructor ... bench: 81,296 ns/iter (+/- 302)
test bench_man_destructor ... bench: 85,756 ns/iter (+/- 164)
只有一个元素,就像你的基准一样:
test bench_auto_destructor ... bench: 69 ns/iter (+/- 1)
test bench_man_destructor ... bench: 67 ns/iter (+/- 2)
但阅读文章到最后。它的解释比我的好。
答案 1 :(得分:3)
作者让你为Link实现自己的drop是因为调用链表上的析构函数不是tail recursive,所以如果非常大C
(即{{1}其节点数大于Rust编译器允许的堆栈帧数超出范围并因此被解除分配,然后当递归调用所有这些drop函数时,您将得到堆栈溢出错误。请阅读上面给出的链接以了解尾递归是什么,但将List
函数替换为List
的{{1}}函数,您就会明白作者为什么要写了你自己的析构函数。
想象一下recsum()
,其中包含1_000_000 Link
。当drop
被取消分配时,您的堆栈将看起来像
List