CLI Framework for Rust: Creating Powerful Console Applications
Rust is a systems programming language that is ideal for creating fast and reliable console utilities. Thanks to strong typing, zero-cost abstractions, and the built-in Cargo package manager, Rust allows you to develop CLI tools that work instantly and require no interpreter. In this article, we will look at three popular CLI frameworks for Rust: clap, structopt, and argh, and show how to use them to create professional console applications.
Why Rust for CLI?
Rust is chosen for CLI development for several reasons:
- Performance — compiled code runs at the level of C/C++.
- Memory safety — the ownership system eliminates leaks and data races.
- Cross-platform — a single codebase works on Windows, macOS, and Linux.
- Ecosystem — thousands of ready-made crates for any task.
1. Clap — Industry Standard
clap (Command Line Argument Parser) is the most popular framework for parsing command-line arguments in Rust. It supports positional arguments, options, flags, subcommands, automatic help generation, and autocompletion for Bash/Zsh/Fish.
Installation
Add to Cargo.toml:
[dependencies]clap = { version = "4.5", features = ["derive"] }Example: Simple Calculator
use clap::Parser;
#[derive(Parser)]#[command(name = "calc")]#[command(about = "Simple CLI calculator")]struct Cli { /// First number a: f64,
/// Second number b: f64,
/// Operation: add, sub, mul, div #[arg(short, long, default_value = "add")] operation: String,}
fn main() { let cli = Cli::parse(); let result = match cli.operation.as_str() { "add" => cli.a + cli.b, "sub" => cli.a - cli.b, "mul" => cli.a * cli.b, "div" => { if cli.b == 0.0 { eprintln!("Error: division by zero"); std::process::exit(1); } cli.a / cli.b } _ => { eprintln!("Unknown operation: {}", cli.operation); std::process::exit(1); } }; println!("Result: {}", result);}Run:
cargo run -- 10 5 --operation mul# Output: Result: 502. Structopt — Elegant Approach
structopt used to be a separate crate, but now its functionality is built into clap via derive macros. However, many older projects still use it directly. Structopt allows you to describe arguments as struct fields with annotations.
Installation (for legacy projects)
[dependencies]structopt = "0.3"Example: File Utility
use structopt::StructOpt;use std::path::PathBuf;
#[derive(StructOpt)]#[structopt(name = "fileutil", about = "File utility")]struct Opt { /// Input file #[structopt(parse(from_os_str))] input: PathBuf,
/// Output file (optional) #[structopt(parse(from_os_str), short = "o", long = "output")] output: Option<PathBuf>,
/// Mode: copy or move #[structopt(short, long, default_value = "copy")] mode: String,
/// Verbose flag #[structopt(short, long)] verbose: bool,}
fn main() { let opt = Opt::from_args(); if opt.verbose { println!("Processing file: {:?}", opt.input); } // Copy/move logic}3. Argh — Minimalism and Speed
argh is a lightweight framework with minimal depend