Rust 中 enum 和 match 是一对非常核心的组合。可以把它理解成:
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
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 + 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 的一个重要区别。
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 可以直接:
因此 Rust 的 enum 更接近:
一个类型 │ ├── Rename + Rename的数据 ├── Select + Select的数据 └── Filter + Filter的数据
再通过:
match op
把不同情况拆开处理。
例如最终可以形成:
然后整个处理流程:
用户参数 ↓ 解析成 ColumnOp ↓ Vec<ColumnOp> ↓ 逐个 match ↓ Rename / Select / Filter ↓ 输出 TSV
这也是 Rust 中 enum + match 非常典型的工程用法。
Github开源生信云平台 DEMO