枚舉
Rust 中的枚舉 enum
是可以表現多種可能變體之一的資料的型別。
每個 enum
中的變體都可以選擇是否要與資料有關聯:
enum Message {
Quit,
ChangeColor(i32, i32, i32),
Move { x: i32, y: i32 },
Write(String),
}
譯註:
Quit
是沒有資料的變體,ChangeColor(i32, i32, i32)
與Write(String)
是有著沒名稱的資料的變體,Move { x: i32, y: i32 }
是著有名稱資料的變體。
定義變體的語法跟定義結構體(struct)的語法類似:你可以定義沒有資料的變體(與類單元結構體類似)、有名稱的資料的變體、或沒有名稱的資料的變體(與多元組結構體類似)。
然而,與各別結構體的定義不同,enum
是單一的型別。
一個枚舉的值可以是任何可能的變量之一。
也因為這個原因,枚舉有時候會被稱為「集合型別」(sum type):枚舉可能值的集合,是所有變體可能值的集合的總和。
我們使用 ::
語法去使用各個變體的名稱:變體的有效範圍存在於 enum
自身的命名中。
因此以下程式碼可以運作:
# enum Message {
# Move { x: i32, y: i32 },
# }
let x: Message = Message::Move { x: 3, y: 4 };
enum BoardGameTurn {
Move { squares: i32 },
Pass,
}
let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };
兩個變體都名為 Move
,但因為它們的有效範圍都各自存在於枚舉的命名中,因此它們可以毫無衝突的使用。
enum
型別的值包含了它是哪個變體的資訊,以及任何與變體有關的資料。
有時候會被當成「tagged union」,因為資料中包含了「tag」去標明它的型別。
編譯器會使用這些資訊去確保你能安全的存取枚舉中的資料。
例如,你不能像解構一個值一樣簡單的解構枚舉中可能變體之一的值:
譯註:因為枚舉中可能的變體不一定恰好是你所想像中的變體。
fn process_color_change(msg: Message) {
let Message::ChangeColor(r, g, b) = msg; // compile-time error
}
不支援這些操作可能看起來像是限制,但我們可以克服這些限制。 有兩種方式:透過自己實作等式(equality)、或藉由下一節會學到的 match 表達式去配對模式(pattern)與變體。 我們對 Rust 還所知不多,所以不知道如何實作等式,但我們會在 traits 一節學到。
建構子當作函式
enum
的建構子(constructor)也可以像函式一樣使用。
例如:
# enum Message {
# Write(String),
# }
let m = Message::Write("Hello, world".to_string());
這與以下程式碼一樣:
# enum Message {
# Write(String),
# }
fn foo(x: String) -> Message {
Message::Write(x)
}
let x = foo("Hello, world".to_string());
這現在對我們來說沒什麼用,但當我們談到 closures 時,我們將會談到將函式作為參數傳遞到其他函式中。
舉例來說,使用 疊代器(iterators),我們可以像這樣把 String
的向量(vector)轉換為 Message::Write
的向量:
# enum Message {
# Write(String),
# }
let v = vec!["Hello".to_string(), "World".to_string()];
let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();
commit 31e39cd