2016-07-06 114 views
6

我有一個枚舉在Rust中有一個值需要String。這可以用這個簡單的例子來說明:如何爲包含字符串的枚舉實現克隆/複製?

#[derive(Clone, Copy)] 
enum Simple { 
    Error(String), 
    Okay, 
    Foo([u32; 5]), 
} 

fn main() { 
    let x = Simple::Error(String::from("blah")); 
    let y = x.clone(); 
} 

枚舉值Foo以上表示I使用採取能夠複製類型或它們的陣列約10其他枚舉。編譯器似乎並不抱怨他們,只能導致這個Error(String)

error: the trait `Copy` may not be implemented for this type; variant `Error` does not implement `Copy` [E0205] 
#[derive(Clone, Copy)] 
       ^~~~ 

note: in this expansion of #[derive_Copy] (defined in src\main.rs) 
help: run `rustc --explain E0205` to see a detailed explanation 

出於某種原因String是不可拷貝。我不明白這一點。我如何爲一個枚舉實現Clone這個只有一個有問題的類型,而其餘的使用默認的impl?

回答

16

Copy指定使按位副本創建有效實例而不使原始實例失效的類型。 String不適用,因爲String包含一個指向堆中字符串數據的指針,並假定它具有該數據的唯一所有權,因此當您丟棄String時,它將釋放堆中的數據,並且如果製作一個String的按位副本,那麼兩個實例都會嘗試釋放同一個內存塊,即未定義的行爲。由於String未執行Copy,enum無法執行Copy或者,因爲編譯器強制Copy類型僅由Copy數據成員組成。

Clone僅提供標準clone方法,每個實現者都要決定如何實現它。 String確實執行Clone,所以你可以#[derive(Clone)]放在你的enum上。

+0

我應該添加我不明白爲什麼字符串不能在原則上或實踐中實現複製特徵 - QT中的QString對象可以被複制並共享和維護內部緩衝區。如果一個副本做了一些可變的事情,那麼首先會克隆緩衝區,以便其他副本仍然保留對原始緩衝區的引用。在QT中,更好的做法是通過引用來避免原子參考計數,但無論如何,副本都是有效的。 – locka

+0

@locka因爲*'Copy'指定了使按位副本創建有效實例*的類型。複製這樣的字符串的位不能增加原子計數器,因爲它不會被複制。你可以選擇保證你需要的東西。您可以[共享所有權](http://doc.rust-lang.org/std/rc/struct.Rc.html),[跨主題共享所有權](http://doc.rust-lang.org/std /sync/struct.Arc.html),實現[clone on write](http://doc.rust-lang.org/std/borrow/enum.Cow.html)等。這對系統來說並不好語言來爲你做出決定(並加重你的代碼)。 – Shepmaster