Course contentsShow
← Rust
Lesson 926 of 1,9107. Error Handling PatternsFree lesson

Designing Error Recovery Policies

Separate policy (what to do on error) from mechanism (how to detect it).

In this lesson, you will learn how to decouple your business logic from recovery strategies by separating the mechanism of failure detection from the policy of how to respond.

When writing robust Rust applications, a common pitfall is hardcoding recovery logic (like retries or logging) directly inside your business functions. This mixes "what happened" with "what to do about it." To design better error recovery policies, we treat the detection as a Result and the policy as a configuration that determines the outcome.

Mechanism vs. Policy

The mechanism is the code that identifies that a failure has occurred (e.g., a network timeout or a checksum mismatch). The policy is the strategy you apply in response: do we retry, do we propagate, or do we degrade gracefully?

By separating these, you can swap recovery strategies without touching the core logic.

// The Mechanism: Detects the failure
fn perform_request() -> Result<Data, NetworkError> {
    // ... logic that returns a Result
}

// The Policy: Decides what to do based on the Error
fn execute_with_retry<F, T, E>(mut action: F) -> Result<T, E> 
where F: FnMut() -> Result<T, E> 
{
    let mut attempts = 0;
    loop {
        match action() {
            Ok(val) => return Ok(val),
            Err(e) if attempts < 3 => attempts += 1, // Policy: Retry 3 times
            Err(e) => return Err(e),
        }
    }
}

Why this matters

If you hardcode a retry loop inside perform_request, you create a "greedy" function that is difficult to test or reuse in contexts where retrying might be dangerous (e.g., non-idempotent operations).

Instead, define your recovery policy as a standalone function or trait. This allows you to pass different policies into your logic: use an "Exponential Backoff" policy in production, but a "Fail Immediately" policy during unit testing. By isolating the policy, you keep your error-handling architecture clean and your functions focused solely on their primary responsibility.

Key Takeaway: Never bake the recovery strategy into the function that detects the failure; instead, treat the policy as an injectable configuration that determines the response to a Result.