2016-03-26 40 views
8

Rust的示例網站上提供了Rust的移動語義示例:Rust Move SemanticsRust如何移動不可複製的堆棧變量?

我對這兩種情況都有基本的瞭解。首先是一個原語如何可以有一個新的別名,原始仍然可以使用,因爲最終的結果是一個複製看到i32利用Copy特質。這對我來說很有意義。

此外,出於許多充分的理由,第二個示例在堆中具有多個引用i32的別名方面是有意義的。 Rust實施所有權規則,因此在創建新綁定後,原始別名不能使用。這有助於防止數據競爭,雙重釋放等。

但是,似乎還有第三種情況沒有提及。 Rust如何實現不執行Copy特性的棧分配結構的移動?這被示爲具有以下代碼:

#[derive(Debug)] 
struct Employee{ 
    age: i32, 
} 

fn do_something(m: Employee){ 
    println!("{:?}", m); 
} 

fn main() { 
    let x = Employee { 
     age: 25, 
    }; 

    do_something(x); 

    //compiler error below because x has moved 
    do_something(x); 
} 

這個我知道:在上面的情況下,鏽病將分配堆棧Employee。上述結構未實現Copy特性,因此在分配給新別名時不會被複制。這對我來說非常混亂,因爲如果Employee結構被分配到堆棧上,並且沒有實現Copy這個特性/它如何移動?它是否物理上移動到do_something()的堆棧框架?

任何幫助,解釋這個難題讚賞。

+1

你介意簡化你的例子嗎?將'Employee'結構變得更加複雜並且至少移除生命週期會很好。例如,'struct Employee {age:i32}'就足夠了。 –

+0

@LukasKalbertodt - 是的,我簡化了這個例子。 –

回答

6

是否物理上移動到do_something()的堆棧幀?

是的。非Copy類型物理移動完全像Copy類型是:與memcpy。你已經明白原型Copy -types被複制到新的位置(例如新的堆棧幀)逐字節。

現在考慮這個實現的Box

struct Box<T> { 
    ptr: *const T, 
} 

當你有

let b = Box::new(27i32); 
do_something(b); // `b` is moved into `do_something` 

那麼i32是在堆上分配和Box保存原始指針指向堆分配的內存。請注意,Box直接(內部的原始指針)直接在堆棧上,而不是在堆上!只有i32在堆上。

Box移動時,就是memcpy ed,就像我剛纔所說的那樣。這意味着堆棧內容被複制(!!)...因此只是指針被逐字節地複製。沒有i32的第二個版本!

當涉及物理移動時,Copy和非Copy類型之間沒有區別。唯一的區別是編譯器對這些類型執行不同的規則。

+4

Minor nit:AIUI,不能保證這些值將被移動,但語義是這樣的,你必須假設它們是。例如,編譯器可以自由地重寫'do_something'來接受對'b'的引用,以防止複製位。重要的是,代碼的行爲就像移動位一樣。 – Shepmaster