Skip to content

Backtrace api and #[from] #157

Open
Open

Description

@steinemann

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
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions