Rust 中 enummatch 是一对非常核心的组合。可以把它理解成:

enum 定义“有哪些可能的状态”,match 根据当前状态分别处理。

1. 最简单的例子

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 的所有情况


2. 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"

3. enum 的每个 variant 可以有完全不同的数据

例如:

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

4. match 还能返回值

例如:

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 的一个重要区别。


5. 可以用 _ 处理其他情况

例如:

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 非常重要的优势。


6. 和 Java 的理解方式对比

你可以暂时把 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

把不同情况拆开处理。


你现在做的 TSV/CSV 工具特别适合这种设计

例如最终可以形成:

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 非常典型的工程用法。