← [ BACK_TO_NOTES ]

使用标准库构建 CLI

不引入第三方 clap 时,用 std::env 与 std::io 构建可测试的小型 CLI 闭环。

#howto #growing #dev #rust

[!info] Related notes

使用标准库构建 CLI

目标

完成一个仅依赖标准库的 CLI:解析单个文件路径、读取文件、以非零退出码表达错误,核心逻辑可单测。本例不从 stdin 读取。

这个场景为什么出现

很多内部工具不需要完整解析器;先掌握 std::env::args_os 与纯函数拆分,再上 clap。

[!abstract] 一句话理解 main 只做参数与 I/O;纯逻辑放库函数;用退出码与 stderr 表达失败。

最小可运行示例

场景:统计文件字节数的 mini wc -c

use std::env;
use std::fs;
use std::process::ExitCode;

// 业务意图:CLI 统计文件字节数。
// 教学点:args_os 保留平台路径;错误到 stderr;退出码。

fn count_bytes(data: &[u8]) -> usize {
    data.len()
}

fn main() -> ExitCode {
    let args: Vec<_> = env::args_os().skip(1).collect();
    let path = match args.as_slice() {
        [arg] if arg == "--help" => {
            println!("usage: bytecount [--] <file>");
            return ExitCode::SUCCESS;
        }
        [path] => path,
        [flag, path] if flag == "--" => path,
        _ => {
            eprintln!("usage: bytecount [--] <file>");
            return ExitCode::from(2);
        }
    };
    match fs::read(path) {
        Ok(bytes) => {
            println!("{}", count_bytes(&bytes));
            ExitCode::SUCCESS
        }
        Err(e) => {
            eprintln!("error reading {}: {e}", path.to_string_lossy());
            ExitCode::FAILURE
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn counts_len() {
        assert_eq!(count_bytes(b"abc"), 3);
    }
}

使用 Rust 1.61+(ExitCode 已稳定)。新建 cargo new bytecount,进入该目录,将代码保存到 src/main.rs;创建内容恰为 abc 的 sample.txt 后运行:

cargo run -- sample.txt   # 输出 3
cargo run -- --help       # 输出用法
cargo run -- -- --help    # 读取文件名为 --help 的文件
cargo test

参数约定:除单独的 --help 外,单个参数按文件路径解释;-- <file> 可显式分隔。缺参或多参退出 2,读文件失败退出 1,成功退出 0。args_os 保留非 UTF-8 路径,只在错误展示时有损转换。返回 ExitCode 允许当前函数正常返回;process::exit 则会跳过栈上析构。

fs::read 一次把整个文件读入内存,适合小文件。这里统计字节而非 Unicode 字符;大文件应按块读取并累计。

结合场景再看三个关注点

  1. 退出码区分用法错误与运行错误
  2. 逻辑可测
  3. 后续可换成 clap 而不改 count_bytes

核心步骤

  1. 定义用户故事与退出码
  2. 拆纯函数
  3. 本例用 args_os 与 fs::read;按需另设计 stdin 支持
  4. 测试 + --help 文案(手写)

工程实践

  • 大工具转 clap
  • 配置见 配置管理
  • 日志别滥用 println 到 stdout 污染管道

2026-10-07:使用 Rust 1.90.0 提取示例编译,验证普通/空文件、非 UTF-8 路径、缺参、多参、缺失文件、帮助和 -- 分隔;内置单测同时通过。

本节总结

  • std 足够做小 CLI
  • 纯逻辑与 I/O 分离
  • 退出码契约

自测题

  1. 为什么核心逻辑不要直接读 env?
  2. 用法错误常用退出码?
参考答案
  1. 便于测试与复用。
  2. 常见 2(惯例,可团队约定)。

延伸阅读与资料来源

资料类型支撑内容
The Book — I/O Project官方书CLI 项目
std::env标准库参数与环境
创建于 2026/7/15 更新于 2026/10/7
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本