clap / Chapter 3 of 5

Output, errors, and exit codes

Handle clap parse errors and exit codes in Rust. Send results to stdout, diagnostics to stderr, and write output files with examples.

2 topicsUpdated
13 / 27

Useful output: stdout, stderr, and files#

Make output predictable so users can pipe data and still see failures.

Write results to stdout and diagnostics to stderr. Return a nonzero exit status on failure. Use --output for a file destination and serde_json for JSON output.

RustWrite text to stdout or a file
use std::{fs, io::{self, Write}, path::PathBuf};
use clap::Parser;

#[derive(Parser)]
struct Cli {
    name: String,
    #[arg(short, long)]
    output: Option<PathBuf>,
}

fn main() -> io::Result<()> {
    let cli = Cli::parse();
    let data = format!("Hello, {}!\n", cli.name);
    match cli.output {
        Some(path) => fs::write(path, data),
        None => io::stdout().write_all(data.as_bytes()),
    }
}
Keep output clean

If output is piped to another program, printing progress text to stdout corrupts the stream. Use eprintln! for progress or errors. Streaming tools need a deliberate broken-pipe policy.

ShellPipe or redirect output
cargo run -- Ada > greeting.txt
cargo run -- Ada --output greeting.txt
cargo run -- Ada | cat
14 / 27

Parse errors and process exit codes#

Clap handles missing, unknown, and malformed arguments before your program logic runs.

Cli::parse() prints help or a parse diagnostic and exits automatically. Use Cli::try_parse_from(...) when testing or embedding a parser and you need to inspect a Result. Help and version are special display errors in the try_ APIs.

RustInspect a parse failure
use clap::{error::ErrorKind, Parser};

#[derive(Debug, Parser)]
struct Cli {
    #[arg(long)]
    port: u16,
}

fn main() {
    match Cli::try_parse_from(["server", "--port", "oops"]) {
        Ok(cli) => println!("{}", cli.port),
        Err(err) => {
            assert_eq!(err.kind(), ErrorKind::ValueValidation);
            err.print().expect("write clap diagnostic");
            std::process::exit(err.exit_code());
        }
    }
}

For your own errors, return Result from main or print a focused diagnostic with eprintln! and exit with a nonzero status. Keep parse errors and work failures conceptually separate: clap validates the command shape; your program handles files, networks, and business rules.

Exit status

Clap help and version normally exit successfully. Invalid arguments exit unsuccessfully. When handling try_parse_from yourself, use err.print() and err.exit_code() to preserve its intended presentation and status.