4.13.枚举

优质
小牛编辑
130浏览
2023-12-01

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

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

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

我们使用::语法来使用每个变量的名字:它们包含在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,不过他们包含在枚举名字中,他们可以无冲突的使用。

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

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

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

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

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

# 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());

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

# enum Message {
# Write(String),
# }

let v = vec!["Hello".to_string(), "World".to_string()];

let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();