【譯】理解Rust中的區域性移動

Praying發表於2020-12-02

原文標題:Understanding Partial Moves in Rust
原文連結:https://whileydave.com/2020/11/30/understanding-partial-moves-in-rust/
公眾號: Rust 碎碎念
翻譯 by: Praying

最近,我一直在研究Rust,雖然從很多方面來看它都是一門十分優秀的語言,但我也發現了很多不易察覺的複雜性。其中一個例子就是,不太引人注意的區域性移動(partial move) 。因此,我在想,為什麼不寫一篇文章來介紹它呢?

所有權(簡潔版)

我不準備在這裡介紹Rust中所有權和借用的全部細節。不過,這裡我們仍然需要一些背景知識使得區域性移動(partial move)能夠講得通。下面通過是一個box和一個箭頭頭來表示Rust中的所有權!

在一個沒有所有權的世界裡(想想Java、C/C++),我們可以通過別名(aliasing)毫無限制地引用資料。我們可以讓兩個引用指向相同的資料,資料之間互相引用等等。

在所有權的世界裡(即Rust),一個引用可以擁有它所指向的資料。在這種情況下,不允許再有對該資料的持有所有權的其他引用(owning references)。例如,如果兩個引用指向相同的資料(即上圖中中間的情況),只有一個引用可以是所有者。

這種對關聯所有權的引用的限制影響了我們寫程式的方式。假定我們試圖從變數p拷貝一份持有所有權的引用到變數q:

這就行不通了,因為它破壞了所有權的不變性 。如果允許這種情況,我們將會對同一份資料持有兩個帶有所有權的引用,而這是不被允許的。所以,我們該怎麼辦呢?我們可以移動(move)它

在上圖中,變數p的值經過移動(move)操作後已經作廢,在對p賦予新的值之前,我們不能再使用它了。當然,這也對我們使用Rust編寫程式產生了相當大的影響。但是,我不準備在這裡討論這個問題。

區域性移動(Partial Moves)

目前為止,我已經看到了一次性移動(move)整個變數(例如,上面中從p移動到q)。此外,我們還可以執行一個區域性移動(partial move),在區域性移動(partial move)中,可以僅移動(move)給定變數的一部分內容。假定現在我們的變數p是一個pair,其中每個元素包含一個持有所有權的引用。然後,我們可以把p中的第二個元素移動到另一個變數q中,如下圖所示:

這個例子的有趣之處在於,不同於之前的情況,儘管變數p的一部分內容已經失效,但它仍然可以以一種受限的方式被使用。具體來講,我們可以使用p.0但是不能使用p.1。此外,Rust阻止我們對變數p進行整個的拷貝(copy)或移動(move)(儘管在我看來,這並沒有必要)。上面的示例轉為程式碼,如下:

fn main() { 
    let mut x = 123;
    let mut y = 456;
    let mut p = (&mut x,&mut y);
    let mut q = p.1;
    ...
}

目前為止,一切都好。但是,當對...進行替換, 比如替換為let mut z = p;時,我們會得到下面的錯誤資訊:

error[E0382]: use of partially moved value: `p`
 --> src/main.rs:6:17
  |
5 | let mut q = p.1;
  |             --- value partially moved here
6 | let mut z = p;
  |             ^ value used here after partial move

只是簡單地告訴我們,我們不能使用一個由於之前的移動(move)操作而已經失效的值。個人而言,我不太理解為什麼Rust阻止這種移動(move)操作,因為能夠很容易地推匯出z只是被部分定義(譯註:即另一部分無效),就像它對p所做的那樣。想必是,儘管可以推導,但是通過某個引用對p賦值必然會導致某些問題吧。

總結

Rust是一門相當酷的語言,但是仍然有很多微妙的特性。希望這篇文章可以幫助解答其中的一個困惑。

相關文章