Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

dropped-error-context

Level: warn · Article: Errors

Every nested level loses context. Every silent catch is a future 3am.

What it checks

.map_err(|_| ..), .or_else(|_| ..) or .unwrap_or_else(|_| ..) whose closure ignores the error it receives: a parameter named _ or starting with _. The original failure, the one with the file name and the OS message, is gone before anyone reads it.

Don’t

#![allow(unused)]
fn main() {
impl Config {
    fn load(path: &Path) -> Result<Self, ConfigError> {
        let text = std::fs::read_to_string(path).map_err(|_| ConfigError::Unreadable)?;
        text.parse()
    }
}
}

“Config unreadable.” Which file? Permission denied, or not found, or a directory? The error that knew is gone.

Do

#![allow(unused)]
fn main() {
enum ConfigError {
    Unreadable {
        path: PathBuf,
        #[source]
        source: std::io::Error,
    },
}

impl Config {
    fn load(path: &Path) -> Result<Self, ConfigError> {
        let text = std::fs::read_to_string(path).map_err(|source| ConfigError::Unreadable {
            path: path.to_path_buf(),
            source,
        })?;
        text.parse()
    }
}
}

Or with thiserror, #[from] and ? do it without a closure at all. The caller matches on your variant; the log walks source() down to the OS.

Silence it

#![allow(unused)]
fn main() {
// rabot: allow(dropped-error-context) the only possible failure is Utf8; the position is what matters
let name = String::from_utf8(bytes).map_err(|_| NameError::NotUtf8 { offset })?;
}