Error Output to stderr
Proper CLI tools distinguish between regular output (stdout) and error messages (stderr) for better shell integration.
Current Problem
Our error messages currently go to stdout:
$ cargo run > output.txt
Problem parsing arguments: not enough arguments
The error appears in output.txt
instead of the terminal, making debugging difficult when output is redirected.
Solution: eprintln!
Macro
use std::env;
use std::error::Error;
use std::fs;
use std::process;
use minigrep::{search, search_case_insensitive};
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::build(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {err}");
process::exit(1);
});
if let Err(e) = run(config) {
eprintln!("Application error: {e}");
process::exit(1);
}
}
pub struct Config {
pub query: String,
pub file_path: String,
pub ignore_case: bool,
}
impl Config {
fn build(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let query = args[1].clone();
let file_path = args[2].clone();
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config {
query,
file_path,
ignore_case,
})
}
}
fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.file_path)?;
let results = if config.ignore_case {
search_case_insensitive(&config.query, &contents)
} else {
search(&config.query, &contents)
};
for line in results {
println!("{line}");
}
Ok(())
}
Testing the Fix
Error case with redirection:
$ cargo run > output.txt
Problem parsing arguments: not enough arguments
Now the error appears on terminal while output.txt
remains empty.
Success case with redirection:
$ cargo run -- to poem.txt > output.txt
Terminal shows nothing, output.txt
contains results:
Are you nobody, too?
How dreary to be somebody!
Summary
This implementation demonstrates:
- Code organization: Clean separation between main.rs and lib.rs
- Error handling: Proper Result types and error propagation
- Testing: TDD approach with isolated, testable functions
- CLI patterns: Command line args, environment variables, proper output streams
- Rust features: Ownership, lifetimes, traits, and error handling
The resulting CLI tool follows Unix conventions and demonstrates production-ready patterns for Rust applications.