2014-04-23 43 views
1

有一個在沿線的標準庫的一些代碼:如何用cast :: forget()生鏽來避免內存泄漏?

/** 
* 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()取得所有權,但不破壞價值,實際上忘記了目標。

兩個很簡單的問題:

  1. 爲什麼這是必要的,而不是讓t掉出的範圍和被破壞?

  2. 爲什麼forget(t)不會導致內存泄漏?

回答

3

如果允許t超出範圍,它將被銷燬。如果該類型具有帶有副作用的析構函數,那麼這是有問題的;例如,假設我們在關閉包含在其自身中的文件句柄的文件上具有析構函數。這意味着在調用swap時,其中一個文件句柄將被關閉,這當然是不合需要的。任何~T也有一個析構函數:它釋放內存。如果你要立即運行析構函數,內存將被釋放,所以你將有一個免費/免費使用的bug。

forget(t)本身並不會導致內存泄漏,因爲在forget內部,它會根據堆棧上的值取其參數。因此,當它返回時,堆棧內存被釋放。如果您忘記~T,則~T確實會泄漏內存;但根本不是這種情況發生的情況,即使您使用作爲~U進行交換,因爲存在以下語義:t只是簡單空間;就在cast::forget(t)調用之前,實際上存在不穩定性,因爲相同的內存由兩個擁有的指針來解決;這就是爲什麼一個人在沒有運行析構函數的情況下被遺忘的原因。

問題的關鍵在於forget應該只用於要移動某個值的位置,因此運行其析構函數的某些東西實際上仍然存在。你不應該在任何其他情況下使用它,否則你會在內存中泄漏內存。