列舉是十分特別的型別,是因為和大部分語言中看起來不太一樣。
最大的不同之一在於:允許每個成員具有不同的型別的屬性
注:本文內容根據<<The rust programmming Language>>有關章節編寫
如果沒有特別說明,"rust學習"系列基本上都是根據此書編寫,不再在後續的篇幅中說明
一、定義、賦值和列印
結論:
示例
#[derive(Debug)] enum simple_enum { Man, Women, Other } //b. 列舉型別中包含資料 #[derive(Debug)] enum color { Red, Green = 3 } //c.列舉型別中包含多個資料,且都是一樣的 #[derive(Debug)] #[derive(PartialEq)] enum grade { A(u32,String), B(u32,String), C(u32,String) } //列舉的方法,定義上了struct方法類似,都是impl,&self impl grade { fn print(&self) { //這個tmd也太麻煩了吧 match self { grade::A(a,b) => println!("{}-{}",a,b), grade::B(a,b) => println!("{}-{}",a,b), grade::C(a,b) => println!("{}-{}",a,b) } } } #[derive(Debug)] enum food<'a>{ 蔬菜([String;5]), 禽肉, 豬肉, 牛羊肉, 海鮮, 大米, 水果(&'a str,&'a str,&'a str) } fn main() { let a = simple_enum::Man; println!("{:?}",a); // 使用println! :?宏列印列舉的時候,變數名稱不能同列舉成員。否則會報錯 let gr = color::Green; println!("{:?}",gr); let r = color::Red; println!("{:?}",r); let grade_a = grade::A(10,String::from("差")); let grade_a1=grade::A(9,String::from("差011")); grade_a.print(); println!("{:?}",grade_a); if (grade_a == grade_a1){ println!("相等"); } else{ println!("不相等"); } let fruit= food::水果("蘋果","香蕉","梨子"); println!("{:?}",fruit); print_first_fruit_member(fruit); let v = food::蔬菜(["白菜".to_string(),"蘿蔔".to_string(),"黃瓜".to_string(),"冬瓜".to_string(),"南瓜".to_string()]); println!("{:?}",v); } fn print_first_fruit_member<'a>(fruit: food<'a>) { if let food::水果(first, second, third) = fruit { println!("{},{},{}", first,second,third); } else { println!("The provided food is not a fruit."); } }
上例中,food允許不同成員有不同的型別值。
二、特別的列舉-Option
Option<T>{
Some(T),
None
}
特殊點在於:
1.定義於標準庫中
2.它甚至被包含在了 prelude 之中,你不需要將其顯式引入作用域
3.它的成員也是如此,可以不需要 Option:: 字首來直接使用 Some 和 None
有用是怎麼體現出來的?
1.首先Option具有許多方法,比如is_some()、is_none()、unwrap()等
2.Option<T>可以作為函式引數,比如fn some_function(option: Option<i32>)
以下是重點(基本上摘抄的):
那麼當有一個 Option<T> 的值時,如何從 Some 成員中取出 T 的值來使用它呢?
Option<T> 列舉擁有大量用於各種情況的方法:
你可以檢視它的文件。熟悉 Option<T> 的方法將對你的 Rust 之旅非常有用(原文)
fn main() { let some_number = Some(5); // some_number的型別是Option<i32> let some_string = Some("a string"); // some_string的型別是Option<&str> let absent_number: Option<i32> = None; // absent_number的型別是Option<i32> println!("some_number is {:?}", some_number); println!("some_string is {:?}", some_string); println!("absent_number is {:?}", absent_number); println!("absent_number is {:?}", absent_number.is_none()); let name:String; name.push_str("abc"); }
非常有用? 還沒有體會出來,先記著!
三、match和列舉
這裡主要討論如何匹配列舉的值,rust的解決方案是match。
1.match某種程度上可以看作rust中if-else的升級版,只不過match有時候可以讓我們少列印一些程式碼而已
match不像java的switch那麼傻瓜(傳統),需要在每個分支上break,否則會繼續執行下一個分支,雖然有那種情況存在,但是絕大部分時候,
我們並不需要break。
rust的match和新版本的java switch寫法更加一致,已經不需要break了,且不會傻乎乎地每個匹配過去
2.順便說一下,rust的創始人很喜歡下劃線。所以下劃分可以分割變數,也可以單獨作為一個變數,但是不建議這麼做
3.關於分支值的匹配
a.rust的分支匹配是窮盡的,意思就是編譯器如果發現你存在一些未匹配的分支,就會報錯。 我們記住這個就可以了,因為編譯器很貼心第處理了這個。
這一點,的確比許多編譯器好多了。
b.rust可以使用多種方式匹配某個分支值,包括單個值、多個值、所有值
c.rust使用下劃線_來表示所有值,這樣我們就可以直接使用這個變數了。這個倒是比許多語言的default稍微省事一些。這個並不是重點
d._和other都可以表示所有值,能一起用,不會編譯錯誤,但是固定有一個分支不可達到。
這意味著,rust的編譯器目前雖然很貼心,但也不會浪費過多的時間去判斷是否具有相同的分支--因為它允許有相同的分支,但是
它的匹配機制使得只會有其中一個分支被執行,其它相同的並會被處理
e.當某個分支值滿足之後,匹配程式碼就結束了,不會像傳統的java switch那麼傻叉繼續執行後面的。這個符合大部分人的思維
示例
/** * 年代列舉 */ #[derive(Debug)] enum Age{Year1970,OtherYear} /** * 硬幣列舉 */ enum Coin{一分(Age),二分,五分,一角,貳角,五角,一元} /** * 文明列舉 */ #[derive(Debug)] enum Civil{中國(Age),歐洲,美國,非洲} fn main(){ let coin = Coin::五分; let one =Coin::一元; match_branch_with_multiple_method(coin); match_branch_with_multiple_method(one); let civil = Civil::中國(Age::Year1970); let _ = Civil::歐洲; //奇特的無意義的變數名稱_,可以用,但是不建議。因為你無法直接傳遞,轉賦也不方便,也不利於維護 //let bad_=_; //這樣會報錯 //match_and_use_branch_value(_) //這樣會報錯 let _歐洲 = Civil::歐洲; match_and_use_branch_value(civil); match_and_use_branch_value(_歐洲); } fn match_branch_with_multiple_method(coin:Coin){ //演示如何匹配多值分支的方式:單個值、多個值、所有值 match coin{ Coin::一分(Age::Year1970) => println!("一分"), //匹配分支特定值 Coin::一分(_) => println!("一分"), //匹配分支所有值 Coin::一分(Age::OtherYear) | Coin::一分(Age::Year1970) => println!("一分"), //匹配分支多個值 Coin::二分 => println!("二分"), Coin::五分 => println!("五分"), Coin::一角 => println!("一角"), Coin::五角 => println!("五角"), other => println!("一元 of other"), _=> println!("一元") }; } fn match_and_use_branch_value(civil:Civil){ //這種方式,可以利用類似匹配元組元素的方式,直接捕獲分支值,並使用 match civil { Civil::中國(age) => println!("有小聰明的中國:{:?},偶爾有一些亮色能拯救他們",age), //自動從civil中捕獲age值 Civil::歐洲 => println!("骯脹但又聰明的歐洲"), _ => println!("{:?}",civil) //如果把這個匹配去掉,那麼會引發編譯錯誤: patterns `Civil::美國` and `Civil::非洲` not covered }; }
如上例,match對列舉的匹配是窮盡的,這意味著一個值沒有匹配,編譯器就報錯,這對於工程師倒是挺友好的!
四、語法糖if let
示例
#[derive(Debug)] enum Age{Year1970,OtherYear} /** * 硬幣列舉 */ enum Coin{一分(Age),二分,五分,一角,貳角,五角,一元} fn main(){ let coin = Coin::一分(Age::Year1970); match coin{ Coin::一分(Age::Year1970) => println!("70特有硬幣?"), _=>println!("不是一分硬幣") } //rust 的語法糖 if let Coin::一分(Age::Year1970) = coin{ println!("70特有硬幣?"); }else{ println!("不是一分硬幣"); } //不帶 else的if let if let Coin::一分(Age::OtherYear)=coin{ println!("不是70特有硬幣"); } }
語法糖這個東西沒有什麼可說的,就是圖方便而已。
如果沒有ide和程式碼助手,那麼的確還是不錯的! 但你如果有ide和程式碼助手,那麼這些語法糖的主要作用就是困擾。
五、小結
總而言之,rust的列舉和大部分語言看起來並沒有什麼本質的上的區別,但的確又有一些區別。
rust發明人的宗旨:讓語言儘量用起來方便一些。 看起來好像得到了體現。 就我個人而言,不是太苟同!
rust的編譯器的大作用,在這裡有了體現!