When implementing error handling, balance between propagation and recovery. Design error types to preserve context while providing users with meaningful ways to handle failure.

For error propagation:

For recovery paths:

// Poor: May panic unexpectedly and loses context
let pos = std.stream_position().unwrap();

// Better: Provides fallback and preserves error context
let pos = std.stream_position().unwrap_or(0);

// Poor: Error type lacks context and proper trait implementation
struct MyError(());

// Better: Full featured error with context preservation
struct MyError<T> {
    inner: T,
    cause: io::Error,
}

impl<T> std::error::Error for MyError<T> {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&self.cause)
    }
}

// Even in examples, show proper error handling
// Poor: Ignores errors
let _ = compress_data(reader).await;

// Better: Shows proper error handling
compress_data(reader).await?;