0x00 前言
通过 Rust 的 Governance 可以看到 Rust 的通过三种方式管理社区:
- RFC process
- Teams
- Working Groups
在 Working Groups 中我发现竟然有一个叫 Command-line interfaces (CLI) working group 的工作组(简称 WG-CLI)。很明显,如果 Rust 对于 CLI 类的应用程序是非常重视的,从编译体积/执行效率/安全性 上都可以作为新生 CLI 类应用开发的首选语言。CLI 类应用主要的输入方式是命令行参数,本文主要讨论 Rust 中处理命令行参数的几种常规方法。
0x01 std::env::args()
Rust 为了适配更多的场景,很多机制都只提供一个极简的可任意扩展的接口,例如支持异步机制时只提供了 Future 这个无法独立运行的概念,而需要引入自己维护的一个第三方 crate 才能使用 async/await 语义。在命令行处理上也是如此,通过 std 只能使用 env::args() 来获取命令行参数,剩下的流程都靠开发者自己实现。
下面是使用 std::env::args() 打印命令行参数的一个示例:
rust
use std::{env, process};
fn print_args(args: impl Iterator<Item = String>) -> Result<(), &'static str> {
for argument in args {
println!("{}", argument);
}
Ok(())
}
fn main() {
print_args(env::args()).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});
}
显然对于简单的命令行参数,这种方式可以在不引入第三方 crate 的情况下实现功能,但是对于一些需要处理复杂输入参数的场景确实比较吃力,剩下的交给 Rust 生态,接下来我们通过一些比较流行的命令行处理 crate 展开学习。
0x02 3rd-party
有一个非常有意思的 Repo ---- argparse-rosetta-rs 对比了几种常见命令行参数处理 crate。
Name | Style | Notes |
---|---|---|
argh | derive |
|
bpaf | Combinatoric or derive |
|
clap_lex | Imperative | No help generation |
clap | Builder or derive |
Color, suggested fixes, completions |
gumdrop | derive |
|
lexopt | Imperative | No help generation |
pico-args | Imperative | No help generation |
xflags | proc-macro |
按照调用风格可以分为两类:
- 命令式 (Imperative)
- 声明式 (Declarative)
命令式方式通常比较直观,将参数解析结构化成支持 iterate and match 的形式。声明式的方式主要有两种,一种是通过链式 Builder 方式声明一个 Command,然后去 match 输入的命令行参数;另一种是通过 Rust 的 derive 机制声明结构体后进行 parse。具体使用方式可以参考上表中每个 crate 文档中的示例。
0x03 结论
Rust 命令行参数 crate 中最流行的 clap 的主要维护者 Ed Page 同时也是 WG-CLI 的 team leader,这种生态模式让 Rust 的生态更加多样性的同时也更具有实践价值,我们从命令行参数处理这一个点来多层次观察 Rust 的语言发展策略,生态构建方式,可以看出,Rust 吸取了前辈的一些成功和失败经验,正在成为一个生命力强劲的编程语言。