标准库中有一些代码如下:
/**
* Swap the values at two mutable locations of the same type, without
* deinitialising or copying either one.
*/
#[inline]
pub fn swap<T>(x: &mut T, y: &mut T) {
unsafe {
// Give ourselves some scratch space to work with
let mut t: T = uninit();
// Perform the swap, `&mut` pointers never alias
ptr::copy_nonoverlapping_memory(&mut t, &*x, 1);
ptr::copy_nonoverlapping_memory(x, &*y, 1);
ptr::copy_nonoverlapping_memory(y, &t, 1);
// y and t now point to the same thing, but we need to completely forget `t`
// because it's no longer relevant.
cast::forget(t);
}
}
事实上,这种“创建临时暂存空间然后忘记它”的模式多次出现。
根据文档 intrinsics::forget()
获取所有权但不会破坏值,有效地忘记了目标。
两个非常简单的问题:
为什么这是必要的,而不是让
t
超出范围并被销毁?为什么
forget(t)
不会导致内存泄漏?
最佳答案
如果 t
被允许超出范围,它将被销毁。如果类型有一个带有副作用的析构函数,那就有问题了;例如,假设我们在一个文件上有一个析构函数,它关闭了文件本身包含的文件句柄。这意味着在 swap
调用中,其中一个文件句柄将被关闭,这当然是不可取的。任何 ~T
也有一个析构函数:它释放内存。如果您要立即运行析构函数,内存将被释放,因此您将遇到释放后使用/双重释放错误。
forget(t)
本身不会导致内存泄漏,因为在 forget
内部,它在堆栈上按值获取参数。因此,当它返回时,堆栈内存被释放。如果您忘记了 ~T
,那么 ~T
确实会泄漏内存;但这根本不是这种情况下发生的事情,即使你将 T
替换为 ~U
,因为语义:t
只是暂存空间;就在 cast::forget(t)
调用之前,实际上是不可靠的,因为同一内存由两个拥有的指针寻址;这就是为什么在不运行析构函数的情况下就简单地忘记了这一点。
问题的关键在于,forget
只应在您要移动值的地方使用,因此将运行其析构函数的东西实际上仍然存在。您不应该在任何其他情况下使用它,否则您会泄漏内存。
关于rust - 你如何避免 cast::forget() 在 rust 中的内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23233712/