2017-03-05 30 views
0

我想要一個鏈接列表並填充它的結構實例,但只有當列表中尚未包含我正在考慮添加的項目時。借用檢查器檢查可變引用的最佳做法是什麼?

我正在處理點,所以如果(3,5)在列表中,我不想添加它,否則我會這樣做。

我當前的代碼:

use std::collections::LinkedList; 

struct Location { 
    x: i32, 
    y: i32, 
} 

fn main() { 
    let mut locations = LinkedList::new(); 

    loop { 
     let location_set = &mut locations; 
     // Scanner stuff happens. 
     if !has_location(location_set, &next_checkpoint_x, &next_checkpoint_y) { 
      let point = Location { 
       x: next_checkpoint_x, 
       y: next_checkpoint_y, 
      }; 
      locations.push_back(point); 
     } 
    } 
} 

fn has_location(location_list: LinkedList<Location>, target_x: &i32, target_y: &i32) -> bool { 
    true // Just until I can figure out this mutability stuff 
} 

我已經能夠得到它通過這些變化來運行,但是這似乎我錯了。

loop { 
    if !has_location(&mut locations, &next_checkpoint_x, &next_checkpoint_y) { 
     // stuff 
    } 
} 
fn has_location(location_list: &LinkedList<Location>, target_x: &i32, target_y: &i32) -> bool { 
    true 
} 

我不想has_location能夠變異鏈表,我只是希望它能夠借用它,以便它可以看看它裏面。我不想考慮has_location(或類似的函數)影響它檢查的鏈表。這就是我創建location_set的原因。我想要以某種只讀的方式引用位置,並將其傳遞給has_location函數,以及引用(位置)的函數在調用has_location函數後不會被銷燬。我在函數的調用中傳入參數&,因爲我不希望傳遞的參數被破壞 - 所以我希望他們借用?

是我想要的東西是有道理的 - 如果我最初聲明位置爲可變鏈表我可以傳遞它的不可變版本的函數來評估?

回答

2

如果你有一個可變的借款,你可以隨時重新借出它作爲一個不可變的借款。這甚至會隱式發生(&mut T將強制爲&T)。因此,您可以直接將location_set直接傳遞給has_location。或者如果你想明確說明這個函數不會改變它的參數,你可以寫&*location_set而不是location_set(儘管我覺得這是不必要的)。

另外,請注意,不可變借用存在時不能使用可變借入;不變的借款在範圍內凍結了數據結構。同樣,當範圍內該變量存在可變借位時,您不能使用變量。在你的第一個代碼示例,你不能引用locationslocation_set在範圍,因爲location_set呈現locations一個可變的借,但是你可以再次使用location_set,因爲push_back只需要一個可變借(它並不需要一個LinkedList按價值)。

僅僅檢查數據結構的函數通常會接收到數據結構的不可變借入。如果數據結構是通過值傳遞的,那麼函數將獲得它的所有權,並因此在返回之前將其銷燬(除非它被移到別處)。因此,是的,你想要has_location接受LinkedList的不可變借款。通過接受不可變的借入(與可變借入相對),編譯器將阻止您修改LinkedList(除非使用不安全的代碼)。

全部放在一起:

use std::collections::LinkedList; 

struct Location { 
    x: i32, 
    y: i32, 
} 

fn main() { 
    let mut locations = LinkedList::new(); 
    let next_checkpoint_x = 0; 
    let next_checkpoint_y = 0; 

    loop { 
     let location_set = &mut locations; 
     // Scanner stuff happens. 
     if !has_location(location_set, &next_checkpoint_x, &next_checkpoint_y) { 
      let point = Location { x: next_checkpoint_x, y: next_checkpoint_y }; 
      location_set.push_back(point); 
     } 
    } 
} 

fn has_location(location_list: &LinkedList<Location>, target_x: &i32, target_y: &i32) -> bool { 
    true 
} 

東西我不理解,雖然在你的榜樣,location_set傳遞給has_location直接(所以函數擁有它,對嗎?)。這在我心中意味着在has_location的範圍內,location_set應該被銷燬,不是嗎? location_set如何繼續存在以用於if塊?

不,has_location不擁有location_set。如果這是其他類型,不實施Copy(例如String),那麼你會是對的,但引用有特殊的規則,使他們更方便使用。

當傳遞給函數的引用,編譯器會自動reborrow引用,以產生一個新的參考,一般具有較短的壽命。在這裏,編譯器重新生成可變引用並生成一個不可變引用;直到不可變引用超出範圍之前,不可變引用才能被使用(不可變引用在這裏沒有綁定到變量,所以你不會注意到這一點)。從概念上講,就好像你將一個不可變的引用傳遞給可變引用(在Rust中,& &mut T不允許你改變T,因爲可能有該外部引用的多個副本),只是兩個引用是「扁平化」。

此外,如果location_set是不可變的,怎麼會是push_back能夠添加到列表的末尾,是因爲它的功能,強制轉換的可變借到一個不變的借?

location_set仍然是一個可變參考(因爲我們使用&mut運算符創建它)。 has_location在不可變引用上運行的事實不會改變location_set是可變引用的事實。一旦對has_location的調用進行了評估,location_set可作爲可變引用重用,因此允許使用變異操作,如push_back

請記住,可變性是Rust純粹是編譯時的概念; mutmut缺少mut只是讓編譯器驗證您的代碼不會執行非法操作,但一旦您的代碼被編譯,這些標記是無處可見的。

+0

這是非常有用的謝謝你。在你的例子中,我不明白的東西,location_set直接傳遞給has_location(所以函數擁有它,對吧?)。在我看來,這意味着在has_location範圍的末尾,location_set應該被銷燬,不是? location_set如何繼續存在以在if塊中使用?另外,如果location_set是不可變的,那麼push_back如何能夠添加到列表的末尾,是因爲函數將可變借用強制爲不可變的借入?謝謝! – neogeek23

+0

在答案結尾處查看我的回覆。 –

相關問題