变体可以持有数据

#![allow(unused)]
fn main() {
enum Status {
    ToDo,
    InProgress,
    Done,
}
}

我们的Status枚举通常被称为C风格枚举。每个变体都是一个简单的标签,有点像命名常量。你可以在许多编程语言中找到这种枚举,如C、C++、Java、C#、Python等。

不过,Rust的枚举可以更进一步。我们可以在每个变体附加数据

变体

假设我们想存储当前正在处理票证的人的名字。只有当票证处于进行中状态时,我们才有此信息。对于待办或已完成的票证则不会有此信息。我们可以通过在InProgress变体上附加一个String字段来模拟这个模型:

#![allow(unused)]
fn main() {
enum Status {
    ToDo,
    InProgress {
        assigned_to: String,
    },
    Done,
}
}

InProgress现在变成了一个类似结构体的变体。语法实际上反映了我们定义结构体时使用的语法——只是作为变体“内联”在枚举中。

访问变体数据

如果我们尝试访问Status实例上的assigned_to

#![allow(unused)]
fn main() {
let status: Status = /* */;

// This won't compile
println!("Assigned to: {}", status.assigned_to);
}

编译器会阻止我们:

error[E0609]: no field `assigned_to` on type `Status`
 --> src/main.rs:5:40
  |
5 |     println!("Assigned to: {}", status.assigned_to);
  |                                        ^^^^^^^^^^^ unknown field

assigned_to特定于变体的,不是所有Status实例都可用。要访问assigned_to,我们需要使用模式匹配

#![allow(unused)]
fn main() {
match status {
    Status::InProgress { assigned_to } => {
        println!("Assigned to: {}", assigned_to);
    },
    Status::ToDo | Status::Done => {
        println!("Done");
    }
}
}

绑定

在匹配模式Status::InProgress { assigned_to }中,assigned_to是一个绑定。我们正在解构Status::InProgress变体并将assigned_to字段绑定到一个新变量,也称为assigned_to。如果我们愿意,我们可以将字段绑定到一个不同的变量名:

#![allow(unused)]
fn main() {
match status {
    Status::InProgress { assigned_to: person } => {
        println!("Assigned to: {}", person);
    },
    Status::ToDo | Status::Done => {
        println!("Done");
    }
}
}

参考资料

  • 本节练习位于 exercises/05_ticket_v2/03_variants_with_data