Course Builder个人学习工作台 · 本地模式
Rust:从所有权到可测试的命令行工具

07|枚举与 Option:明确处理不同情况

目标:用枚举和穷尽匹配处理有限状态,避免把“没有结果”伪装成正常值。

命令行工具经常面对几个互斥选择:显示字节数或显示字符数、拿到了参数或没有参数。枚举把这些选择明确写进类型中。结构体要求多个字段同时存在,枚举则从若干变体中选择一种;变体还可以携带数据。

标准库的 Option<T> 就是这样的枚举:Some(T) 带着一个值,None 表示没有值。这里的 <T> 表示里面的值可以是不同类型;本课只需要会使用,第 10 课再自己定义泛型接口。match 匹配变体,并要求覆盖所有可能情况。少处理一种状态会在编译时暴露,而不是等到某位用户遇到空输入。

把缺省选择写明白

创建 examples/options.rs:

enum Metric {
    Bytes,
    Chars,
}

fn measure(text: &str, metric: Metric) -> usize {
    match metric {
        Metric::Bytes => text.len(),
        Metric::Chars => text.chars().count(),
    }
}

fn main() {
    let text = "你好 Rust";
    let first = text.split_whitespace().next();
    match first {
        Some(word) => println!("first={word}"),
        None => println!("first=<empty>"),
    }
    println!("bytes={}", measure(text, Metric::Bytes));
    println!("chars={}", measure(text, Metric::Chars));
}

next() 从迭代器取下一项,可能没有,所以结果是 Option<&str>。Some(word) 一边识别变体,一边把里面的文本引用绑定为 word。=> 右边是对应分支的表达式。

运行前先预测:没有词时,代码会返回空字符串还是进入另一个分支?

cargo run --quiet --example options

预期输出:

first=你好
bytes=11
chars=7

两个统计分支都返回 usize,因此整个 match 表达式可以作为函数返回值。枚举让调用者不能传入一个拼错的模式字符串;字符串到枚举的解析应在输入边界处理。

验证空输入不是意外

  1. 把 text 改为 " "。
  2. 再运行,第一行应为 first=<empty>,后两行为 bytes=3、chars=3。
  3. 暂时删掉 None 分支,运行 cargo check --example options,应报告模式未覆盖完整。
  4. 恢复分支和原输入。空白字符串有长度,却没有按空白分隔的词,这两件事并不矛盾。

独立练习:新增 Metric::Words,并在 measure 中处理。使用断言验证 measure("你好 Rust", Metric::Words) 为 2、measure("", Metric::Words) 为 0。

参考修改

完整替换原枚举及函数,原 main 保留并在末尾加入题目中的两条断言:

enum Metric {
    Bytes,
    Chars,
    Words,
}

fn measure(text: &str, metric: Metric) -> usize {
    match metric {
        Metric::Bytes => text.len(),
        Metric::Chars => text.chars().count(),
        Metric::Words => text.split_whitespace().count(),
    }
}

这是一段依赖原 main 的替换代码。两条完整断言为 assert_eq!(measure("你好 Rust", Metric::Words), 2); 和 assert_eq!(measure("", Metric::Words), 0);。同一运行命令成功且仍输出原来的三行,即通过验收。

如果你想用 unwrap() 直接取出 Some,先确认 None 是否确实不可能发生。用户输入为空是正常情况,应明确处理。如果添加枚举变体后编译失败,顺着诊断补齐各处 match,这正是枚举帮助你发现遗漏的方式。对需要随新增变体一起更新的逻辑,避免过早用 _ 吞掉其余情况。

本课新增 examples/options.rs。最终 CLI 也会用 Option 检查参数是否存在。

参考资料