Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backtrace api and #[from] #157

Open
steinemann opened this issue Oct 20, 2021 · 0 comments
Open

Backtrace api and #[from] #157

steinemann opened this issue Oct 20, 2021 · 0 comments

Comments

@steinemann
Copy link

steinemann commented Oct 20, 2021

Hi,

I discovered a little strange behavior when using the experimental backtrace api together with thiserror.

I have prepared an example gist and the playground

You get different behavior when trying to access the backtrace of an error behind an enum.

When the enum value is derived with from , without specifying an own backtrace member (as tuple)
the backtrace() api call from the Error trait will return None

When the enum value has an own backtrace member, the backtrace() api call will return the backtrace of the underlying error.
Accessing the backtrace value of the enum value is not possible anymore.

From my POV the best way would be that the backtrace() api call should always return the uppermost backtrace if it is specified. Because it would be still possible to use the source() api call to get to the inner error.

Is the current behavior a bug or expected ?

Thanks,
Peter

Putting the code example here as reference also:

#![feature(backtrace)]
#![feature(backtrace_frames)]

use std::{backtrace::Backtrace, error::Error};

use thiserror::Error as ThisError;

#[derive(ThisError, Debug)]
#[error("MyError")]
struct MyError {
    backtrace: std::backtrace::Backtrace,
}

#[derive(ThisError, Debug)]
#[error("OtherError")]
struct OtherError {
    backtrace: std::backtrace::Backtrace,
}

#[derive(ThisError, Debug)]
enum AllErrors {
    #[error("E")]
    E(#[from] MyError),

    #[error("O")]
    O(#[from] OtherError, Backtrace),
}

fn main() {
   let my = MyError {
        backtrace: std::backtrace::Backtrace::capture(), // Backtrace location A
    };
    assert!(my.backtrace().is_some());

    let other = OtherError {
        backtrace: std::backtrace::Backtrace::capture(), // Backtrace location B
    };
    assert!(other.backtrace().is_some());

    let all_my = AllErrors::from(my);
    let all_other = AllErrors::from(other); // Implicit Backtrace C

    println!("My my {:#?}", all_my.backtrace()); // -> None
    println!("My my source {:#?}", all_my.source().unwrap().backtrace()); // -> Backtrace A

    println!("Other my {:#?}", all_other.backtrace()); // -> Backtrace B
    println!(
        "Other my source {:#?}",
        all_other.source().unwrap().backtrace() // Backtrace B
    );
    
    // Inconsistency:
    // Option 1 : Always return inner backtrace
    // all_my.backtrace() should return Backtrace A
    // all_other.backtrace() should return Backtrace B // as
    
    // Option 2: Always deliver own backtrace
    // all_my.backtrace() should return None
    // all_other.backtrace() should return Backtrace C ( where all_other was created )
    
    // The prefered option is Option 2 as currently there is no way to access backtrace C
    // using the backtrace interface
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant