In this lesson, you will learn how to shift from "fail-fast" error handling to accumulating non-fatal warnings into a structured report for batch processing.
The Problem with Fail-Fast
In many systems, we rely on the ? operator to short-circuit execution the moment an error occurs. While perfect for critical failures, this approach is frustrating for bulk operations—like validating a batch of user configuration files—where you want to see all the validation errors at once rather than fixing them one-by-one in a loop.
The Pattern: The Error Accumulator
To solve this, we move away from Result<T, E> as an immediate return type and instead adopt a collector pattern. We create an "Error Report" object that serves as a container for non-fatal issues encountered during processing.
struct ValidationReport {
errors: Vec<String>,
}
impl ValidationReport {
fn new() -> Self { Self { errors: Vec::new() } }
fn record(&mut self, error: String) {
self.errors.push(error);
}
fn is_empty(&self) -> bool { self.errors.is_empty() }
}
Implementing Batch Processing
Instead of returning a Result early, you pass a mutable reference to your accumulator into your logic functions. This allows the process to continue even after a minor failure is registered.
fn process_configs(configs: Vec<Config>) -> Result<Vec<Config>, ValidationReport> {
let mut report = ValidationReport::new();
let mut valid_configs = Vec::new();
for config in configs {
if let Err(e) = validate(&config) {
report.record(format!("Config {}: {}", config.id, e));
} else {
valid_configs.push(config);
}
}
if report.is_empty() {
Ok(valid_configs)
} else {
Err(report)
}
}
By decoupling the detection of an error from the termination of the process, you provide significantly better feedback to the user. This pattern is essential for CLI tools, bulk data importers, and any system where "partial success" is a valid and desired state. When using this, ensure that the errors collected are truly non-fatal—if an error compromises the integrity of subsequent operations, you must still revert to immediate Result propagation.
Key Takeaway: Use an error accumulation object to collect and report multiple non-fatal issues, enabling batch processing and comprehensive user feedback instead of short-circuiting on the first failure.