Rust clap — 구조체 하나로 명령줄 옵션과 --help 를 만든다
clap 은 Rust 명령줄 도구의 옵션을 읽는 크레이트다. 구조체에 #[derive(Parser)] 를 붙이면 칸이 인자·옵션이 되고, 문서 주석이 --help 문장이 된다. 잘못된 값은 clap 이 막고 종료 코드 2 로 끝낸다. 입문 34·47편의 wordfreq 에 --top 을 달아 본다.
01 구조체가 곧 옵션 정의다
derive 기능을 켜고 붙인다.
[package]
name = "wordfreq"
version = "0.3.0"
edition = "2024"
[dependencies]
clap = { version = "=4.6.7", features = ["derive"] }use clap::Parser;
use std::collections::HashMap;
use std::fs;
/// Count the most frequent words in a text file.
#[derive(Parser)]
#[command(version)]
struct Args {
/// File to read
file: String,
/// How many words to show
#[arg(short, long, default_value_t = 10)]
top: usize,
/// Keep upper and lower case apart
#[arg(long)]
case_sensitive: bool,
}
fn main() {
let args = Args::parse();
let text = fs::read_to_string(&args.file).unwrap_or_default();
let mut counts: HashMap<String, usize> = HashMap::new();
for raw in text.split_whitespace() {
let word = raw.trim_matches(['.', ',', '!', '?']);
let word = if args.case_sensitive { word.to_string() } else { word.to_lowercase() };
*counts.entry(word).or_insert(0) += 1;
}
let mut pairs: Vec<_> = counts.into_iter().collect();
pairs.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
for (word, n) in pairs.into_iter().take(args.top) {
println!("{n:>3} {word}");
}
}#[derive(Parser)]를 붙인 구조체의 칸 하나하나가 명령줄 항목이다.Args::parse()가 실제 인자를 읽어 구조체를 채운다. 입문 34편처럼env::args()를 손으로 뒤지지 않는다.- 이름표 없는 칸(
file)은 위치로 받는 인자다. 반드시 줘야 한다. #[arg(short, long)]은-t와--top두 이름으로 받는다.default_value_t = 10은 안 주면 10 이다. 타입이usize라서 숫자로 바꾸는 것까지 clap 이 한다.- 타입이
bool인 칸은 값을 받지 않는 스위치다.--case-sensitive를 쓰면true다. ///로 쓴 문서 주석이 도움말 문장이 된다.
02 --help 는 공짜다
cargo run -- --helpCount the most frequent words in a text file
Usage: wordfreq [OPTIONS] <FILE>
Arguments:
<FILE> File to read
Options:
-t, --top <TOP> How many words to show [default: 10]
--case-sensitive Keep upper and lower case apart
-h, --help Print help
-V, --version Print version구조체와 주석만 썼는데 사용법·인자·옵션·기본값이 정리된 도움말이 나왔다. --version 은 #[command(version)] 이 Cargo.toml 의 버전으로 채운다. 옵션을 더하면 도움말도 저절로 바뀌니, 코드와 도움말이 어긋날 일이 없다.
03 실행과 잘못된 값
cargo run -- sample.txt --top 3 Running `target/debug/wordfreq sample.txt --top 3`
8 the
5 is
4 and입문 34편의 sample.txt 에서 상위 3개만 나왔다. 숫자 자리에 글자를 넣으면 어떻게 될까.
cargo run -- sample.txt --top three Running `target/debug/wordfreq sample.txt --top three`
error: invalid value 'three' for '--top <TOP>': invalid digit found in string
For more information, try '--help'.우리 코드는 한 줄도 돌지 않았다. clap 이 'three' 가 --top 에 맞지 않는 값이라고(invalid value) 막고, 이유(숫자가 아닌 글자)와 --help 안내를 찍었다. 이때 종료 코드는 2 다. 명령줄 도구에서 2 는 "사용법이 틀렸다" 는 관례적인 값이라, 입문 34편에서 손으로 process::exit(2) 하던 일을 clap 이 대신 해 준다.
04 실습 — 옵션 하나 더하기
최소 글자 수보다 짧은 단어를 빼는 --min-len 옵션을 더해 보자.
Args에#[arg(long, default_value_t = 1)] min_len: usize를 더한다.- 세는 반복에서
word.chars().count() < args.min_len이면continue한다(입문 21편, 한글은chars()로 센다). cargo run -- --help로 도움말에 저절로 들어갔는지 본다.
명령줄 도구의 다음 단계는 하위 명령(git commit 처럼)이다. clap 은 enum 에 #[derive(Subcommand)] 를 붙여 만든다. 다음 편은 웹 API 서버를 만드는 axum 이다.