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

Error Propagation in Recursive Functions

Manage recursive depth failures explicitly within the Result return type.

In this lesson, you will learn how to maintain safety and control when your recursive functions encounter errors by explicitly managing the stack depth as a part of your Result return type.

When recursion meets Result, a common pitfall is losing track of why a failure occurred deep in the call stack. If you simply use the ? operator, the error propagates upward, but the context of the specific recursion depth is often discarded. For tasks like tree traversal or recursive parsing, you may want to differentiate between a structural error in your data and a "stack overflow" or "depth limit" error.

To manage this, define an error variant that explicitly tracks the recursion limit. Instead of letting the program silently fail or panic, you pass a depth counter through your arguments.

Implementing Explicit Depth Tracking

#[derive(Debug)]
enum RecursiveError {
    DepthExceeded,
    InvalidData(String),
}

fn process_tree(node: &Node, depth: usize) -> Result<u32, RecursiveError> {
    // 1. Explicitly check depth before recursing
    if depth > 100 {
        return Err(RecursiveError::DepthExceeded);
    }

    // 2. Propagate errors from recursive calls normally
    let left = process_tree(&node.left, depth + 1)?;
    let right = process_tree(&node.right, depth + 1)?;

    Ok(left + right + node.value)
}

In this pattern, the recursion depth is not just a counter; it is a constraint enforced by the Result type. If process_tree returns Err(RecursiveError::DepthExceeded), the caller knows exactly that the tree is too deep, rather than guessing if the data itself was corrupted.

When working with deep recursions, you must decide if a "depth exceeded" error is a fatal application state or a recoverable condition. By treating the depth as a domain-specific error, you enable higher-level functions to handle the failure gracefully—perhaps by pruning the tree or switching to an iterative approach—rather than allowing a stack overflow to trigger a panic!. Always prefer explicit depth constraints in your function signature to keep your error handling deterministic and predictable.

Key Takeaway: Always include depth-tracking as an explicit error variant in your recursive functions to transform uncontrolled stack exhaustion into predictable, handleable Result types.