2017-02-25 101 views
3

假設我們有一個看起來像這樣的枚舉:有沒有一種簡單的方法來突變Rust中的枚舉字段?

enum MyEnum { 
    Field1, 
    Field2 {x: f64, y: f64}, 
    /* Maybe some other fields */ 
    MyString(String), 
} 

現在我創造了這個枚舉亞型MyString的一個實例和一些動作後,我想它發生變異。例如:

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    /* Some actions */ 
    // Mutating the string 
    match my_enum { 
     MyEnum::MyString(ref mut content) => { 
      content.push('!'); 
     }, 
     _ => {} 
    } 
    // Printing the string 
    match my_enum { 
     MyEnum::MyString(content) => { 
      println!("{}", content); 
     }, 
     _ => {} 
    } 
} 

然而,以這樣的方式匹配是相當繁瑣的,當我們從上下文準確地知道my_enum可以只有MyString。我寧願寫這樣的事情(不正確的鏽語法):

[email protected]('!'); 
println!("{}", [email protected]); 

如果,假設,my_enum是亞型Field2的,然後發生變異x

[email protected] += 1.0; 

我能做些什麼喜歡這個?我強烈地想,答案是「否」,因爲如果我刪除從上面的比賽_ => {},類型檢查開始抱怨非詳盡的模式匹配:

patterns `Field1` and `Field2` not covered 

儘管它可以推斷my_enum只能是MyString。通過「推斷」,我的意思是編譯器可以跟蹤所有類型的變量MyEnum它們可以精確包含哪些值的子類型。

我在一個更大的代碼中找到了一個可以方便使用的地方,但我想我可以用其他方法重寫它。不過,我認爲編譯器可能更聰明,並且至少要明白,在這種情況下,MyEnum::MyString模式是詳盡無遺的。如果上述問題的答案真的是「否」,正如我懷疑的那樣,如果Rust開發人員討論了這個問題(也許是RFCS鏈接?),並且是否值得提出功能請求,我很感興趣。

+0

'如果讓MyEnum :: MyString的(參考MUT含量)= {my_enum content.push( '!'); }' – ildjarn

回答

4

從Rust 1.15.1開始,編譯器將無法識別特定變量在特定執行點只能是enum的特定變體。因此,你總是需要寫一個詳盡的match就可以了。

然而,一些開發商鏽have been considering使得它使得每個enum變種將自己的類型,這將是enum本身的亞型。

如果你的變種有很多數據字段,或者如果你連有方法,你可以考慮在struct包裹enum變種的領域,並直接使用struct,完全繞過枚舉,直到你需要枚舉不管是什麼原因。如果只有幾個字段,並且不需要在枚舉上調用方法,那麼您可以通過將一個可變指針保留在開始時獲得的每個字段中來獲得詳盡的match,就像這樣:

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    let content = 
     match my_enum { 
      MyEnum::MyString(ref mut content) => content, 
      _ => unreachable!(), 
     }; 

    /* Some actions */ 
    // Mutating the string 
    content.push('!'); 

    // Printing the string 
    println!("{}", content); 
} 
6

如果你的代碼的整個部分,其中的變量是已知的具有特定類型,你可以只把這些代碼的match裏面,或者如果只有一個match您關心的手臂,使用if let

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    /* Some actions */ 
    if let MyEnum::MyString(ref mut content) = my_enum { 
     content.push('!'); 
     //... 
     println!("{}", content); 
    } 
} 

或者,如果它只是冗長match(或if let),這是問題,你可以寫的方法,使之更爲簡潔:

impl MyEnum { 
    fn push(&mut self, char c) { 
     if let MyEnum::MyString(ref mut content) = *self { 
      content.push(c); 
     } else { 
      unreachable!(); 
     } 
    } 

    // In practice print might be more generic, for example implement 
    // Display 
    fn print(&self) { 
     if let MyEnum::MyString(ref content) = *self { 
      println!("{}", content); 
     } 
    } 
} 

fn main() { 
    //... 
    my_enum.push('!'); 
    my_enum.print(); 
} 
相關問題