Parallel Processing

For CPU-intensive tasks, you can use the rayon crate for parallel processing:

use rayon::prelude::*;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
use walkdir::WalkDir;

#[derive(StructOpt, Debug)]
struct Opt {
    #[structopt(parse(from_os_str))]
    directory: PathBuf,
    
    #[structopt(short, long)]
    pattern: String,
}

fn search_file(path: &Path, pattern: &str) -> Result<Vec<String>, std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    let matches: Vec<String> = content
        .lines()
        .enumerate()
        .filter(|(_, line)| line.contains(pattern))
        .map(|(i, line)| format!("{}:{}: {}", path.display(), i + 1, line))
        .collect();
    
    Ok(matches)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    
    // Find all files in the directory
    let files: Vec<PathBuf> = WalkDir::new(&opt.directory)
        .into_iter()
        .filter_map(Result::ok)
        .filter(|e| e.file_type().is_file())
        .map(|e| e.path().to_owned())
        .collect();
    
    println!("Searching {} files for '{}'...", files.len(), opt.pattern);
    
    // Search files in parallel
    let results: Vec<String> = files
        .par_iter()
        .filter_map(|path| {
            match search_file(path, &opt.pattern) {
                Ok(matches) if !matches.is_empty() => Some(matches),
                _ => None,
            }
        })
        .flatten()
        .collect();
    
    // Print results
    for line in results {
        println!("{}", line);
    }
    
    Ok(())
}

This example implements a simple parallel grep-like tool that searches for a pattern in all files in a directory.


Best Practices for CLI Applications

Based on experience from large Rust projects, here are some best practices for building command-line applications:

1. Follow the Unix Philosophy

  • Do one thing and do it well
  • Process text streams as the universal interface
  • Make composition easy
  • Make output suitable for both humans and machines

2. Provide Good Documentation

  • Include comprehensive help text
  • Document all options and arguments
  • Provide examples of common use cases
  • Consider adding a man page for Unix-like systems

3. Be Consistent with Platform Conventions

  • Use double dashes for long options (--option)
  • Use single dashes for short options (-o)
  • Follow platform-specific conventions for file paths, line endings, etc.

4. Handle Errors Gracefully

  • Provide clear error messages
  • Use appropriate exit codes
  • Don’t crash on invalid input
  • Log errors to stderr, not stdout

5. Respect the Environment

  • Honor environment variables
  • Use configuration files in standard locations
  • Don’t modify files without permission
  • Clean up temporary files

6. Provide Feedback

  • Show progress for long-running operations
  • Use colors and formatting judiciously
  • Support both interactive and non-interactive modes
  • Provide verbose and quiet options

Conclusion

Rust is an excellent choice for building command-line applications, offering a combination of performance, safety, and ergonomics that few other languages can match. With its rich ecosystem of libraries and tools, you can create sophisticated CLI applications that are both user-friendly and robust.

The key takeaways from this guide are:

  1. Use dedicated libraries like clap and structopt for argument parsing
  2. Handle errors gracefully with Result, ?, and crates like anyhow and thiserror
  3. Provide good user experience with clear help text, progress indicators, and interactive input
  4. Test thoroughly to ensure your application works as expected
  5. Optimize for distribution to make your application accessible to users

By following these principles and leveraging Rust’s strengths, you can build command-line applications that are a joy to use and maintain. Whether you’re creating simple utilities or complex tools, Rust provides the foundation for building reliable, efficient CLI applications that stand the test of time.