列舉
YouTube 上觀看本章內容: Part 1, Part 2, Part 3 及 Part 4
enum
是列舉(enumeration)的簡稱。它們看起來與結構體非常相似,但又有所不同。區別有:
- 當你想要一個東西和另一個東西時,使用
struct
。 - 當你想要一個東西或另一個東西時,請使用
enum
。
所以,結構體是用於多個事物在一起,而列舉則是用於多個選擇在一起。
要宣告列舉時,寫下 enum
,並用程式碼區塊將包含的選項用逗號分隔。就像 struct
一樣,最後一部分的逗號則可有可無。我們將建立一個名為 ThingsInTheSky
的列舉:
enum ThingsInTheSky { Sun, Stars, } fn main() {}
這是個列舉,因為你可以看到太陽或星星:你必須選擇一個。這些叫做變體(variants)。
// 建立兩個選擇的列舉 enum ThingsInTheSky { Sun, Stars, } // 有這個函式我們可以用i32來建立ThingsInTheSky。 fn create_skystate(time: i32) -> ThingsInTheSky { match time { 6..=18 => ThingsInTheSky::Sun, // 介於6到18小時之間我們可以見到太陽 _ => ThingsInTheSky::Stars, // 除此之外,我們可以見到星星 } } // 有這個函式我們可以匹配到ThingsInTheSky的兩個選擇。 fn check_skystate(state: &ThingsInTheSky) { match state { ThingsInTheSky::Sun => println!("I can see the sun!"), ThingsInTheSky::Stars => println!("I can see the stars!") } } fn main() { let time = 8; // 這是 8 點鐘 let skystate = create_skystate(time); // create_skystate回傳ThingsInTheSky check_skystate(&skystate); // 給它參考那麼它就能讀到變數skystate }
印出 I can see the sun!
。
你也可以將資料新增到列舉中。
enum ThingsInTheSky { Sun(String), // 現在每個變體都有字串 Stars(String), } fn create_skystate(time: i32) -> ThingsInTheSky { match time { 6..=18 => ThingsInTheSky::Sun(String::from("I can see the sun!")), // 這裡寫下字串 _ => ThingsInTheSky::Stars(String::from("I can see the stars!")), } } fn check_skystate(state: &ThingsInTheSky) { match state { ThingsInTheSky::Sun(description) => println!("{}", description), // 給字串命名為description那麼我們就能使用它 ThingsInTheSky::Stars(n) => println!("{}", n), // 或你能命名成 n。或其它任何東西──它無關緊要 } } fn main() { let time = 8; // 這是 8 點鐘 let skystate = create_skystate(time); // create_skystate 回傳 ThingsInTheSky check_skystate(&skystate); // 給它參考那麼它就能讀到變數skystate }
印出來的結果一樣:I can see the sun!
。
你也可以"匯入(import)"一個列舉,這樣你就不用打那麼多字了。下面這個例子裡,我們每次在匹配我們的 mood 時都要輸入 Mood::
:
enum Mood { Happy, Sleepy, NotBad, Angry, } fn match_mood(mood: &Mood) -> i32 { let happiness_level = match mood { Mood::Happy => 10, // 我們每次都要輸入 Mood:: Mood::Sleepy => 6, Mood::NotBad => 7, Mood::Angry => 2, }; happiness_level } fn main() { let my_mood = Mood::NotBad; let happiness_level = match_mood(&my_mood); println!("Out of 1 to 10, my happiness is {}", happiness_level); }
印出的是 Out of 1 to 10, my happiness is 7
。讓我們匯入,這樣我們就可以少打點字了。要匯入所有的東西時寫做 *
。注意:它和反參考關鍵字的 *
一樣,但完全不同。
enum Mood { Happy, Sleepy, NotBad, Angry, } fn match_mood(mood: &Mood) -> i32 { use Mood::*; // 我們匯入Mood裡的所有東西。現在我們可以只寫Happy、Sleepy等變體名。 let happiness_level = match mood { Happy => 10, // 我們不用再寫 Mood:: 了 Sleepy => 6, NotBad => 7, Angry => 2, }; happiness_level } fn main() { let my_mood = Mood::Happy; let happiness_level = match_mood(&my_mood); println!("Out of 1 to 10, my happiness is {}", happiness_level); }
enum
的一部分也可以轉變成整數。這是因為 Rust 給 enum
提供了以 0 開頭的數字給每個分支各自使用。如果你的列舉中沒有任何其他資料的話,你可以拿它來做些事情。
enum Season { Spring, // 如果這是 Spring(String) 或其它東西,它就不能這樣用 Summer, Autumn, Winter, } fn main() { use Season::*; let four_seasons = vec![Spring, Summer, Autumn, Winter]; for season in four_seasons { println!("{}", season as u32); } }
印出:
0
1
2
3
不過如果你想的話,你也可以給它一個不同的數字──Rust 並不在意,可以用同樣的方式來使用它。只要在你想要有數值的變體加上 =
和數字。你不必給數字到所有變體。但如果你不這樣做,Rust 就會給變體從前一個分支數字加 1 的數字。
enum Star { BrownDwarf = 10, RedDwarf = 50, YellowStar = 100, RedGiant = 1000, DeadStar, // 想想看這個數字會有多少? } fn main() { use Star::*; let starvec = vec![BrownDwarf, RedDwarf, YellowStar, RedGiant]; for star in starvec { match star as u32 { size if size <= 80 => println!("Not the biggest star."), // 記得: size 沒有任何意思。只不過是我們為了可以列印所選的名稱 size if size >= 80 => println!("This is a good-sized star."), _ => println!("That star is pretty big!"), } } println!("What about DeadStar? It's the number {}.", DeadStar as u32); }
印出:
Not the biggest star.
Not the biggest star.
This is a good-sized star.
This is a good-sized star.
What about DeadStar? It's the number 1001.
DeadStar
本來是 4 號,但現在是 1001。
使用多種型別的列舉
你知道向量、陣列等等之中的元素都需要相同的型別(只有 tuple 不同)。但其實你可以用列舉來放不同的型別。想象一下,我們想要有個向量,有 u32
或 i32
。當然,你可以做出 Vec<(u32, i32)>
(帶有 (u32, i32)
元組的向量),但是我們想要每次只有一種。所以這裡可以使用列舉。這是簡單的範例:
enum Number { U32(u32), I32(i32), } fn main() {}
所以這有兩個變體:U32
變體裡有 u32
,I32
變體裡有 i32
。U32
和 I32
只是我們取的名字。它們可以取名叫 UThirtyTwo
、IThirtyTwo
或其他任何東西。
現在,如果我們把它們放到向量中,我們就會有 Vec<Number>
,因為都是同一個型別編譯器會很開心。編譯器並不在乎我們有的是 u32
或者是 i32
,因為它們都在一個叫做 Number
的單一型別裡面。因為它是列舉,你必須選擇一種,這就是我們想要的。我們將使用 .is_positive()
方法來挑選。如果是 true
,那麼我們將選擇 U32
,如果是 false
,那麼我們將選擇 I32
。
現在程式碼像這樣:
enum Number { U32(u32), I32(i32), } fn get_number(input: i32) -> Number { let number = match input.is_positive() { true => Number::U32(input as u32), // 如果是正數改成 u32 false => Number::I32(input), // 不然就給數字因為它已經是 i32 }; number } fn main() { let my_vec = vec![get_number(-800), get_number(8)]; for item in my_vec { match item { Number::U32(number) => println!("It's a u32 with the value {}", number), Number::I32(number) => println!("It's an i32 with the value {}", number), } } }
印出了我們想看到的結果:
It's an i32 with the value -800
It's a u32 with the value 8