枚举

enums.md
commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2

Rust 中的一个enum是一个代表数个可能变量的数据的类型。enum中的每个变量都可选是否关联数据:

  1. enum Message {
  2. Quit,
  3. ChangeColor(i32, i32, i32),
  4. Move { x: i32, y: i32 },
  5. Write(String),
  6. }

定义变量的语法与用来定义结构体的语法类似:你可以有不带数据的变量(像类单元结构体),带有命名数据的变量,和带有未命名数据的变量(像元组结构体)。然而,不像单独的结构体定义,一个enum是一个单独的类型。一个枚举的值可以匹配任何一个变量。因为这个原因,枚举有时被叫做“集合类型”:枚举可能值的集合是每一个变量可能值的集合的总和。

我们使用::语法来使用每个变量的名字:它们包含在enum名字自身中。这样的话,以下的情况都是可行的:

  1. # enum Message {
  2. # Move { x: i32, y: i32 },
  3. # }
  4. let x: Message = Message::Move { x: 3, y: 4 };
  5. enum BoardGameTurn {
  6. Move { squares: i32 },
  7. Pass,
  8. }
  9. let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };

这两个变量都叫做Move,不过他们包含在枚举名字中,他们可以无冲突的使用。

枚举类型的一个值包含它是哪个变量的信息,以及任何与变量相关的数据。这有时被作为一个“标记的联合”被提及。因为数据包括一个“标签”表明它的类型是什么。编译器使用这个信息来确保安全的访问枚举中的数据。例如,我们不能简单的尝试解构一个枚举值,就像它是其中一个可能的变体那样:

  1. fn process_color_change(msg: Message) {
  2. let Message::ChangeColor(r, g, b) = msg; // This causes a compile-time error.
  3. }

不支持这些操作(比较操作)可能看起来更像限制。不过这是一个我们可以克服的限制。有两种方法:我们自己实现相等(比较),或通过match 表达式模式匹配变量,你会在下一部分学到它。我们还不够了解Rust如何实现相等,不过我们会在特性找到它们。

构造器作为函数(Constructors as functions)

一个枚举的构造器总是可以像函数一样使用。例如:

  1. # enum Message {
  2. # Write(String),
  3. # }
  4. let m = Message::Write("Hello, world".to_string());

与下面是一样的:

  1. # enum Message {
  2. # Write(String),
  3. # }
  4. fn foo(x: String) -> Message {
  5. Message::Write(x)
  6. }
  7. let x = foo("Hello, world".to_string());

这对我们没有什么直接的帮助,直到我们要用到闭包时,这时我们要考虑将函数作为参数传递给其他函数。例如,使用迭代器,我们可以这样把一个String的vector转换为一个Message::Write的vector:

  1. # enum Message {
  2. # Write(String),
  3. # }
  4. let v = vec!["Hello".to_string(), "World".to_string()];
  5. let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();