2013-10-21 20 views
4

我正在使用生鏽0.8。在關閉中調用堆內函數參數

爲什麼我能做到這一點:

fn add(num: ~int) -> ~fn(int) -> int { |x| 
    *num + x 
} 

但不是這樣的:

fn outer(num: ~int) -> ~fn(int) -> int { |x| 
    *inner(num) + x 
} 

fn inner(num: ~int) -> ~int { 
    num 
} 

第二個失敗,「錯誤:無法遷出的被捕獲外層變量的堆關閉」。什麼使得調用一個特殊的功能?

是否擔心內部函數可能會對靜態分析無法捕獲的盒裝函數執行某些操作?

+2

不是一個正確的答案,所以我會添加它作爲評論︰https://github.com/mozilla/rust/wiki/Doc-under-construction-FAQ –

回答

3

問題是兩次調用閉包的可能性。第一次運行時,捕獲的變量num被移入inner,即移出封閉環境。然後,在第二個電話中,num所在的地方現在無效(因爲它被移出),這打破了記憶的安全性。

更詳細,可以把閉包(約)

struct Closure { // stores all the captured variables 
    num: ~int 
} 

impl Closure { 
    fn call(&self, x: int) -> int { 
     // valid: 
     *self.num + x 

     // invalid: 
     // *inner(self.num) + x 
    } 
} 

希望這使得它更清晰:在無效的一個,一個人試圖從借來的指針後面移出self.numinner通話(在此之後,它完全與num字段斷開)。如果這是可能的,則self將保持無效狀態,因爲例如self.num上的析構函數可能已經被調用,這釋放了存儲器(違反存儲器安全性)。的這個


一個分辨率爲「一次函數」,它被實現但隱藏在編譯器標記,因爲它們可能有利於被移除(在它的最基本的)調整的上述call類型爲fn call(self, x: int),即調用閉包移動self這意味着你可以移出環境(因爲call然後擁有self及其字段),也意味着該函數是靜態保證只能被調用一次*。 *如果封閉環境沒有轉移所有權,例如:如果是struct Env { x: int }

+0

這似乎我不應該在乎如何函數被使用,只要我把它轉換成的閉包擁有相同的生命週期。爲什麼我不能只添加生命週期參數並使其工作?除非它是可變的並且不是冪等的,否則多次調用一個函數沒有任何內在的危險。 此外,爲什麼將函數移入內部將其移出封閉環境?這似乎是安全的,因爲我知道關閉何時會被釋放。它應該保持在同一個環境中。 – nnythm

+0

這不是問題的關閉。這是'num'。 'num'捕獲被移動,'〜'指針在第一次調用後被釋放(因爲它被移入內部,所以閉包的環境失去了對'num'的控制和所有權,即它不會當'num'被丟棄時,去「選擇」)。這意味着第二個(和第三個,第四個......)會在處理num時嘗試使用已釋放的內存。 – huon