_ 处理其他情况Rust 中 enum 和 match 是一对非常核心的组合。可以把它理解成:
enum定义“有哪些可能的状态”,match根据当前状态分别处理。
enum Direction {
Up,
Down,
Left,
Right,
}
fn move_player(direction: Direction) {
match direction {
Direction::Up => println!("向上"),
Direction::Down => println!("向下"),
Direction::Left => println!("向左"),
Direction::Right => println!("向右"),
}
}
fn main() {
move_player(Direction::Up);
move_player(Direction::Left);
}
这里:
enum Direction {
Up,
Down,
Left,
Right,
}
定义了一个 Direction 类型,它只有四种可能:
Direction::Up
Direction::Down
Direction::Left
Direction::Right
然后:
match direction {
Direction::Up => ...,
Direction::Down => ...,
Direction::Left => ...,
Direction::Right => ...,
}
相当于:
如果是 Up → 做 A
如果是 Down → 做 B
如果是 Left → 做 C
如果是 Right → 做 D
而且 Rust 要求 match 尽可能覆盖 enum 的所有情况。
这才是 enum + match 真正强大的地方。
例如定义一个命令:
enum Command {
Rename(String, String),
Select(Vec<String>),
Filter(String),
}
它可以表示三种不同的操作:
Command::Rename("old".to_string(), "new".to_string())
Command::Select(vec![
"SNP".to_string(),
"P".to_string(),
])
Command::Filter("P < 1e-5".to_string())
然后:
fn execute(command: Command) {
match command {
Command::Rename(old, new) => {
println!("rename {} -> {}", old, new);
}
Command::Select(columns) => {
println!("select {:?}", columns);
}
Command::Filter(expression) => {
println!("filter {}", expression);
}
}
}
调用:
fn main() {
execute(Command::Rename(
"old_name".to_string(),
"new_name".to_string(),
));
execute(Command::Filter(
"P < 1e-5".to_string(),
));
}
这里 match 不仅判断是哪一个 variant,还把里面的数据取出来:
Command::Rename(old, new)
对应:
Command::Rename("SNP", "Marker")
↓ match
old = "SNP"
new = "Marker"
例如:
enum ColumnOp {
Rename {
from: String,
to: String,
},
Select {
columns: Vec<String>,
},
Filter {
expression: String,
},
}
这种写法在你现在设计 TSV/CSV column operation 时其实非常合适。
然后:
fn apply(op: ColumnOp) {
match op {
ColumnOp::Rename { from, to } => {
println!("rename {} -> {}", from, to);
}
ColumnOp::Select { columns } => {
println!("select {:?}", columns);
}
ColumnOp::Filter { expression } => {
println!("filter {}", expression);
}
}
}
使用:
let op = ColumnOp::Filter {
expression: "P < 1e-5".to_string(),
};
apply(op);
例如:
enum Operation {
Add,
Subtract,
Multiply,
}
fn calculate(op: Operation, a: i32, b: i32) -> i32 {
match op {
Operation::Add => a + b,
Operation::Subtract => a - b,
Operation::Multiply => a * b,
}
}
那么:
let result = calculate(Operation::Add, 10, 20);
println!("{}", result);
输出:
30
注意这里:
match op {
Operation::Add => a + b,
Operation::Subtract => a - b,
Operation::Multiply => a * b,
}
整个 match 本身就是一个表达式,可以产生值。
这也是 Rust 和很多语言 switch 的一个重要区别。
_ 处理其他情况例如:
enum Status {
Running,
Finished,
Failed,
}
fn message(status: Status) {
match status {
Status::Running => println!("运行中"),
Status::Finished => println!("完成"),
_ => println!("其他状态"),
}
}
不过对于你自己定义的 enum,通常更推荐明确写出每个 variant:
match status {
Status::Running => ...,
Status::Finished => ...,
Status::Failed => ...,
}
这样以后增加:
enum Status {
Running,
Finished,
Failed,
Cancelled,
}
编译器会提醒你:
non-exhaustive patterns
也就是说:
你增加了一种状态,但忘记处理它了。
这实际上是 Rust enum + match 非常重要的优势。
你可以暂时把 Rust:
enum ColumnOp {
Rename { from: String, to: String },
Select { columns: Vec<String> },
Filter { expression: String },
}
理解成一种比 Java enum 更强大的枚举。
Java 常见的是:
enum Operation {
RENAME,
SELECT,
FILTER
}
数据通常需要另外定义:
class RenameOperation {
String from;
String to;
}
而 Rust 可以直接:
enum ColumnOp {
Rename {
from: String,
to: String,
},
Select {
columns: Vec<String>,
},
Filter {
expression: String,
},
}
因此 Rust 的 enum 更接近:
一个类型
│
├── Rename + Rename的数据
├── Select + Select的数据
└── Filter + Filter的数据
再通过:
match op
把不同情况拆开处理。
例如最终可以形成:
enum ColumnOp {
Rename {
from: String,
to: String,
},
Select {
columns: Vec<String>,
},
Filter {
expression: String,
},
}
然后整个处理流程:
用户参数
↓
解析成 ColumnOp
↓
Vec<ColumnOp>
↓
逐个 match
↓
Rename / Select / Filter
↓
输出 TSV
这也是 Rust 中 enum + match 非常典型的工程用法。