标准库中有一些代码如下:
/**
* 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)
不会导致内存泄漏?
答案 0 :(得分:3)
如果t
被允许超出范围,它将被销毁。如果类型具有带有副作用的析构函数,则会出现问题;例如,假设我们在文件上有一个析构函数,用于关闭包含在其中的文件句柄。这意味着在swap
调用时,其中一个文件句柄将被关闭,这当然是不可取的。任何~T
都有一个析构函数:它释放内存。如果你要立即运行析构函数,内存将被释放,所以你将有一个免费后使用/双免费的bug。
forget(t)
本身不会导致内存泄漏,因为在forget
内,它在堆栈上按值获取其参数。因此,当它返回时,堆栈存储器被释放。如果你忘了~T
,那~T
确实会泄漏记忆;但由于语义原因,即使您将T
作为~U
进行交换,这也不是在这种情况下发生的事情:t
只是划痕空间;就在cast::forget(t)
调用之前,实际上存在不健全性,因为相同的内存由两个拥有的指针解决;这就是为什么一个人在没有运行析构函数的情况下被遗忘的原因。
问题的关键是forget
只应该用于移动值的地方,因此运行其析构函数的东西确实仍然存在。您不应该在任何其他情况下使用它,或者可以泄漏内存。