Does the error line number not automatically appear in the result?

I tried it before, and the error info didn't show which line it was on. Even when using thiserror and anyhow, it didn't show up. Is it supposed to be like that?

I tried to manually capture the backtrace to get the line number info

use std::borrow::Cow;
use std::fmt;
use std::error::Error as StdError;
use std::io::Error as StdIoError;

#[cfg(feature = "backtrace")]
use std::backtrace::Backtrace;

#[derive(Copy, Clone)]
enum Error {
    WrongInput,
    IoError
}

const ERROR: &[&'static str] = &[
    "Wrong Input",
    "IO Error"
];

struct AppError {
    error: Error,
    detail: Cow<'static, str>,
    
    #[cfg(feature = "backtrace")]
    backtrace: Backtrace
}

impl AppError {
    fn new<T>(error: Error, detail: T) -> Self 
    where
        T: Into<Cow<'static, str>>
    {
        Self { 
            error,
            detail: detail.into(),
            
            #[cfg(feature = "backtrace")]
            backtrace: Backtrace::capture()
        }
    }
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let error = ERROR[self.error as usize];
        
        #[cfg(not(feature = "backtrace"))]
        return write!(f, "{} {}", error, self.detail);
        
        #[cfg(feature = "backtrace")]
        return write!(f, "{} {}\nBacktrace : {}", error, self.detail, self.backtrace);
    }
}

impl fmt::Debug for AppError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    fmt::Display::fmt(self, f)
  }
}

impl StdError for AppError {}

impl From<StdIoError> for AppError {
    fn from(err: StdIoError) -> Self {
        AppError::new(Error::IoError, err.to_string())
    }
}

fn test(input: i32) -> Result<i32, AppError> {
    if input % 2 == 0 {
        return Ok(input * 2);
    }
    
    Err(AppError::new(Error::WrongInput, "Input is not divisible by 2"))
}

fn main() -> Result<(), AppError> {
    let _test = test(1)?;
    
    let _test2 = std::fs::read("no_file.txt")?;
    
    Ok(())
}

Without Backtrace capture :

RUST_BACKTRACE=1 cargo run      
     
Error: Wrong Input Input is not divisible by 2   

With Backtrace capture :

RUST_BACKTRACE=1 cargo run --features backtrace

Error: Wrong Input Input is not divisible by 2
Backtrace :    0: tes::AppError::new
             at ./src/main.rs:38:24                      1: tes::test
             at ./src/main.rs:74:9
   2: tes::main                                                    at ./src/main.rs:78:17
   3: core::ops::function::FnOnce::call_once
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   4: std::sys::backtrace::__rust_begin_short_backtrace                                                                  at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158:18
   5: std::rt::lang_start::{{closure}}
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18
   6: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/core/src/ops/function.rs:287:21
   7: std::panicking::catch_unwind::do_call
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:590:40
   8: std::panicking::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:553:19
   9: std::panic::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panic.rs:359:14
  10: std::rt::lang_start_internal::{{closure}}
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/rt.rs:175:24
  11: std::panicking::catch_unwind::do_call
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:590:40
  12: std::panicking::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:553:19
  13: std::panic::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panic.rs:359:14
  14: std::rt::lang_start_internal
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/rt.rs:171:5
  15: std::rt::lang_start
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:205:5
  16: main
  17: <unknown>
  18: __libc_start_main
  19: _start

Also is there anything missing or wrong with my error handling approach?

Yes. Per default, when returning an error from main(), its Display Debug output is written to stderr and an erroneous exit code is returned.

Thank youuu, that’s good to know

Update : It turns out Anyhow can show the line numbers. I just forgot to set the environment flag. However, thiserror still doesn't show them. Is this by design in Thiserror, or am I doing something wrong in my code?

Thiserror :

use thiserror::Error;

#[derive(Error, Debug)]
enum AppError {
    #[error("invalid value")]
    Invalid,
}

fn run(x: i32) -> Result<i32, AppError> {
    if x < 0 {
        Err(AppError::Invalid)
    } else {
        Ok(x)
    }
}

fn main() -> Result<(), AppError> {
    let _ = run(-1)?;
    
    Ok(())
}

Result :

RUST_BACKTRACE=1 cargo run --bin thiserror                                             

Error: Invalid                                         

Anyhow :

use anyhow::{Result, anyhow};

fn run(x: i32) -> Result<i32> {
    if x < 0 {
        Err(anyhow!("invalid value"))
    } else {
        Ok(x)
    }
}

fn main() -> Result<()> {
    run(-1)?;
    
    Ok(())
}

Result :

RUST_BACKTRACE=1 cargo run --bin anyhow                                                
                 
Error: invalid value
                                                      Stack backtrace:                                         0: anyhow::error::<impl anyhow::Error>::msg
             at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.101/src/backtrace.rs:27:14
   1: anyhow::__private::format_err
             at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.101/src/lib.rs:687:13
   2: anyhow::run
             at ./src/bin/anyhow.rs:5:13                 3: anyhow::main
             at ./src/bin/anyhow.rs:12:5
   4: core::ops::function::FnOnce::call_once                       at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   5: std::sys::backtrace::__rust_begin_short_backtrace
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158:18
   6: std::rt::lang_start::{{closure}}
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18
   7: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/core/src/ops/function.rs:287:21
   8: std::panicking::catch_unwind::do_call
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:590:40
   9: std::panicking::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:553:19
  10: std::panic::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panic.rs:359:14
  11: std::rt::lang_start_internal::{{closure}}
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/rt.rs:175:24
  12: std::panicking::catch_unwind::do_call
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:590:40
  13: std::panicking::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:553:19
  14: std::panic::catch_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panic.rs:359:14
  15: std::rt::lang_start_internal
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/rt.rs:171:5
  16: std::rt::lang_start
             at /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:205:5
  17: main
  18: <unknown>
  19: __libc_start_main
  20: _start

Per default, when returning an error from main(), its Display output is written to stderr and an erroneous exit code is returned.

The Termination trait uses Debug when printing an error.

There is no impl<T: Termination, E: Display> Termination for Result<T, E>.

2 Likes

thiserror doesn't change your error type and hence can't include a Backtrace field in it. It's goal is to implement various traits for it such as Error, Display and From to make error handling easier.

You can however manually add a Backtrace field to your enum variant, and you can even get thiserror to initialize it for you if the error variant wraps another error marked with #[from].

1 Like

Thank youuu for the answer