Rust 我们需要为链表手动创建析构函数吗?

Rust 我们需要为链表手动创建析构函数吗?,rust,Rust,我正在阅读,我不明白为什么要使用链表(堆栈) 我认为当列表值超出范围时,列表本身和所有节点都会被清除。只是为了示范吗 我对有无手动析构函数的版本进行了基准测试,发现“无析构函数”版本的性能更好: 用于1..30000000中的uu{ 让mut list=list::new(); 列表。推送(1); assert_eq!(list.pop(),Some(1)); } 使用手动析构函数: real 0m11.216s 用户0m11.192s 系统0.020s 没有手动析构函数: real 0m9

我正在阅读,我不明白为什么要使用链表(堆栈)

我认为当列表值超出范围时,列表本身和所有节点都会被清除。只是为了示范吗

我对有无手动析构函数的版本进行了基准测试,发现“无析构函数”版本的性能更好:

用于1..30000000中的uu{
让mut list=list::new();
列表。推送(1);
assert_eq!(list.pop(),Some(1));
}
使用手动析构函数:

real 0m11.216s
用户0m11.192s
系统0.020s
没有手动析构函数:

real 0m9.071s
用户0m9.044s
系统0m0.004s

你是对的。名单会自动清理干净的。正如提交人所说:

所有这些都是自动为我们处理的。。。一帆风顺

然后,他们解释了为什么自动处理不好: 自动销毁过程对列表头调用
drop
,然后对第一个元素调用
drop
。等等等等

这是一个函数调用一个函数调用一个函数(有无限可能的重复),它迟早会炸毁堆栈

此测试导致堆栈溢出:

#[测试]
fn构建真正的大堆栈(){
让mut stack=List::new();
对于0..1_000_000中的i{
堆栈推送(i);
}
}
如果为两个版本使用
--release
构建,则表明它们的性能几乎相同:

#[工作台]
fn台架自动销毁装置(b:&mut台架){
b、 国际热核实验堆{
让mut list=list::new();
对于0..1000中的i{
列表推送(i);
}
assert_eq!(list.pop(),Some(999));
});
}
#[法官席]
fn工作台\手动销毁装置(b:&多工作台){
b、 国际热核实验堆{
让mut list=ManualDestructorList::new();
对于0..1000中的i{
列表推送(i);
}
assert_eq!(list.pop(),Some(999));
});
}
测试台自动自毁器。。。实验台:81296纳秒/国际热核实验堆(+/-302)
测试台_人_自毁器。。。实验台:85756纳秒/国际热核实验堆(+/-164)
只有一个元素,就像在基准测试中一样:

测试台自动自毁器。。。试验台:69纳秒/国际热核实验堆(+/-1)
测试台_人_自毁器。。。实验台:67纳秒/国际热核实验堆(+/-2)

把这篇文章读到最后,它的解释比我的好。

作者让您实现自己的drop for Link的原因是在链表上调用析构函数是不正确的,因此如果链表非常大(即,
列表
的节点计数大于Rust编译器允许的堆栈帧数)超出范围,因此被解除分配,然后当递归调用所有这些drop函数时,将出现堆栈溢出错误。请阅读我上面给出的链接,了解什么是尾部递归,但替换
recsum()
函数,使用
链接
drop
函数,您就会明白作者为什么让您编写自己的析构函数

设想一个
列表
有1个
节点
。当该
列表
被解除分配时,堆栈将如下所示

(Stack Frame #1) List.drop();    // all work is done in this frame, so no need to save stack frame
(Stack Frame #2) self.ptr.drop(); self.ptr.deallocate(); // uh oh! stack frame must be preserved throughout all recursive calls because deallocate must be called after self.ptr.drop() returns
(Stack Frame #3) self.ptr.drop(); self.ptr.deallocate();
...
(Stack Frame #1_000_000) self.ptr.drop(); self.ptr.deallocate(); // we'll never reach here, because a stack overflow error would have been occurred long ago

我仍然无法理解它的含义。rust编译器默认的清理方式是不好的?对于每个函数调用,编译器需要引入一些存储寄存器的开销。(参见示例)。这会在堆栈上分配内存。如果这种情况太频繁,堆栈会溢出:是的,我知道递归函数调用会消耗资源。但默认情况下,列表和节点没有关联的析构函数。它只是释放内存。因此,默认情况下,不会发生函数调用以进行销毁,不是吗?事实上,您可以尝试对列表操作进行基准测试使用和不使用显式析构函数时,您可以看到默认的析构函数(不使用析构函数)具有更好的性能。因此,我询问将析构函数与列表关联的原因。“列表和节点没有关联的析构函数”这不是一个正确的语句。它们确实有一个析构函数,即在
Box
实例上调用
Drop
的析构函数。而且,没有“仅仅释放内存”;内存释放仍然是一个函数调用。Box有一个由rust提供的析构函数。但不管怎样,为什么我要删除“impl Drop for List”做一些基准测试,它有更好的性能?我终于明白了这个例子中析构函数背后的原因。我注意到这里有越来越多的直截了当的答案,所以大多数都不能提供理解整个问题的基础,而导致了它的解决方案。谢谢,伙计。