ideasman42
ideasman42

Reputation: 48278

How to trace the cause of an error result?

When writing code that uses Result type, you may want different behavior for users and developers.

If you make a unique error it's not hard to search for it, but if the error is from the standard library, the error may be very generic.

For example, it's impossible to know which read command caused an unexpected end-of-file without manually changing every file.read()? to file.read().unwrap().

Is there a convenient way to get a stack-trace from a Result?

A weak but workable solution could be to make a macro for reading, read_in_release_unwrap_in_debug!(file, data)... but this feels very awkward.


I have a file reader with many read calls and one fails. I'm not sure which. At run-time, I want to push the result back to the caller. For debugging, I want the failed read call to stop or somehow let me know its line number.

Upvotes: 62

Views: 25934

Answers (3)

Timmmm
Timmmm

Reputation: 97138

If you use anyhow you can get this for free! You enable an environment variable:

RUST_BACKTRACE=1 cargo +nightly run

Also enable the backtrace crate feature.

Upvotes: 23

Chris Emerson
Chris Emerson

Reputation: 14061

A result by itself doesn't have any backtrace information, but you can add it to custom error types.

The error_chain crate (which is unfortunately no longer maintained) is an example which generates an error type for you, for which you get backtrace generation for free when the RUST_BACKTRACE environment variable is set.

You could also use the backtrace library directly and do it yourself.

Upvotes: 8

comphilip
comphilip

Reputation: 550

I resolve this requirement with easy-error crate, while error-chain crate is fine also.

Use Result defined in easy-error as return type, then use context method to convert other Result types.

Most important thing is to pass information to context method with line number.

use easy_error::{Result, ResultExt};
use std::path::PathBuf;

fn test_open() -> Result<()> {
    let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    p.push("resources/test/songs.json");
    File::open(p).context(format!("{}:{}", file!(), line!()))?;
    Ok(())
}

To avoid typing format!("{}:{}", file!(), line!()) all the time, define a macro:

#[macro_export]
macro_rules! code_loc {
    () => {
        format!("{}:{}", file!(), line!())
    };
}

And finally code will be:

File::open(p).context(code_loc!())?;

Upvotes: 4

Related Questions