2016-09-17 67 views
3

我有一個相當簡單的程序:是否可以返回在函數範圍內創建的引用?

fn f<'a>() -> &'a i32 { 
    &1 
} 

fn main() { 
    println!("{}", f()); 
} 

它不會編譯(某些輸出消隱):

$ rustc test.rs 
test.rs:2:6: 2:7 error: borrowed value does not live long enough 
test.rs:2  &1 

我弄清失敗的原因。

  1. 我不知道如何返回在函數作用域內創建的引用。有沒有辦法做到這一點?
  2. 爲什麼一輩子不能被單一回報消滅?

編輯:我改變了標題,因爲它提示返回盒裝類型將有助於哪些不是(見答案)。

+1

*我明白爲什麼它失敗* - 我不認爲這是完全正確的:-)如果你這樣做,你也應該明白爲什麼你不能這樣做。作爲一個思想實驗,如果你用''a'參數化了''a''來調用'f'會出現什麼情況? – Shepmaster

回答

2

由於Rust使用RAII風格的資源管理,只要程序離開作用域,該範圍內所有未移動的值都將被破壞。價值必須生活在某個地方,以便參考有效。因此,要麼返回值(如果您擔心在執行此操作時有額外的副本,那麼不要擔心,因爲該副本將被優化掉)或將其復位並返回該框。除非你是如下返回一個靜態分配的字符串作爲&str,你根本無法返回一個「新」(呼叫者)參考:

fn f<'a>() -> &'a str { 
    "yo" 
} 
+0

返回靜態值還是使其成爲返回字段的方法,使f有效的唯一方法是?還是有另一個用於定義像'fn f <'a>() - >&'a T'這樣的函數的用例?是否記錄了副本優化,我如何確定它會被執行? – kopiczko

+0

@kopiczko有一個非常隱晦的例子,'fn foo <'a>() - >''T'比'&'static更靈活,但它與它可以返回的內容無關。至於副本:複製'i32' *與複製'&i32'一樣廉價或便宜*!事實上,大多數類型都很小,通常不值得花時間去關心它被複制的頻率。 – delnan

+0

@delnan我同意擔心複製i32是無意義的。但是說,我有一個20個字段的結構,並根據條件在循環中創建它1M次。我想提取創建函數來保持代碼乾燥。那麼有必要擔心複製,不是嗎? – kopiczko

2

拳擊參考不會幫助。在大多數方面,Box<T>實際上與未裝箱的T相同,包括所有權和生命期問題。根本的問題是一旦函數返回,局部變量就會停止存在。因此,對於局部變量的引用將指向調用函數獲取該引用時的釋放內存。將包裝紙放在參考文件上並不能解決這個問題。

我認爲這是一個簡化的例子,它是從一個真正的程序中提煉出來的。由於信息不足,我無法給出有針對性的建議,但通常是根據價值返回東西(即,在這種情況下僅爲-> i32)而非參考是一個非常好的主意。

+0

你說得對,Box無能爲力。我錯誤地認爲它會作爲參考計數器。 – kopiczko

1

由於Rust 1.21,一個名爲rvalue static promotion新功能意味着在問題代碼沒有現在編譯。

在這個例子中,因爲1是一個常數,所以編譯器將它提升爲static,這意味着返回的引用具有'static的生存期。該函數去加糖看起來類似:

fn f<'a>() -> &'a i32 { 
    static ONE: i32 = 1; 
    &ONE 
} 

這適用於任何編譯時間常數,包括結構:

struct Foo<'a> { 
    x: i32, 
    y: i32, 
    p: Option<&'a Foo<'a>> 
} 

fn default_foo<'a>() -> &'a Foo<'a> { 
    &Foo { x: 12, y: 90, p: None } 
} 

但這不會編譯:

fn bad_foo<'a>(x: i32) -> &'a Foo<'a> { 
    /* Doesn't compile as x isn't constant! */ 
    &Foo { x, y: 90, p: None } 
} 
相關問題