Code Monkey home page Code Monkey logo

thiserror's Introduction

derive(Error)

github crates.io docs.rs build status

This library provides a convenient derive macro for the standard library's std::error::Error trait.

[dependencies]
thiserror = "1.0"

Compiler support: requires rustc 1.56+


Example

use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("data store disconnected")]
    Disconnect(#[from] io::Error),
    #[error("the data for key `{0}` is not available")]
    Redaction(String),
    #[error("invalid header (expected {expected:?}, found {found:?})")]
    InvalidHeader {
        expected: String,
        found: String,
    },
    #[error("unknown data store error")]
    Unknown,
}

Details

  • Thiserror deliberately does not appear in your public API. You get the same thing as if you had written an implementation of std::error::Error by hand, and switching from handwritten impls to thiserror or vice versa is not a breaking change.

  • Errors may be enums, structs with named fields, tuple structs, or unit structs.

  • A Display impl is generated for your error if you provide #[error("...")] messages on the struct or each variant of your enum, as shown above in the example.

    The messages support a shorthand for interpolating fields from the error.

    • #[error("{var}")] ⟶ write!("{}", self.var)
    • #[error("{0}")] ⟶ write!("{}", self.0)
    • #[error("{var:?}")] ⟶ write!("{:?}", self.var)
    • #[error("{0:?}")] ⟶ write!("{:?}", self.0)

    These shorthands can be used together with any additional format args, which may be arbitrary expressions. For example:

    #[derive(Error, Debug)]
    pub enum Error {
        #[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)]
        InvalidLookahead(u32),
    }

    If one of the additional expression arguments needs to refer to a field of the struct or enum, then refer to named fields as .var and tuple fields as .0.

    #[derive(Error, Debug)]
    pub enum Error {
        #[error("first letter must be lowercase but was {:?}", first_char(.0))]
        WrongCase(String),
        #[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)]
        OutOfBounds { idx: usize, limits: Limits },
    }
  • A From impl is generated for each variant that contains a #[from] attribute.

    The variant using #[from] must not contain any other fields beyond the source error (and possibly a backtrace — see below). Usually #[from] fields are unnamed, but #[from] is allowed on a named field too.

    #[derive(Error, Debug)]
    pub enum MyError {
        Io(#[from] io::Error),
        Glob(#[from] globset::Error),
    }
  • The Error trait's source() method is implemented to return whichever field has a #[source] attribute or is named source, if any. This is for identifying the underlying lower level error that caused your error.

    The #[from] attribute always implies that the same field is #[source], so you don't ever need to specify both attributes.

    Any error type that implements std::error::Error or dereferences to dyn std::error::Error will work as a source.

    #[derive(Error, Debug)]
    pub struct MyError {
        msg: String,
        #[source]  // optional if field name is `source`
        source: anyhow::Error,
    }
  • The Error trait's provide() method is implemented to provide whichever field has a type named Backtrace, if any, as a std::backtrace::Backtrace. Using Backtrace in errors requires a nightly compiler with Rust version 1.73 or newer.

    use std::backtrace::Backtrace;
    
    #[derive(Error, Debug)]
    pub struct MyError {
        msg: String,
        backtrace: Backtrace,  // automatically detected
    }
  • If a field is both a source (named source, or has #[source] or #[from] attribute) and is marked #[backtrace], then the Error trait's provide() method is forwarded to the source's provide so that both layers of the error share the same backtrace. The #[backtrace] attribute requires a nightly compiler with Rust version 1.73 or newer.

    #[derive(Error, Debug)]
    pub enum MyError {
        Io {
            #[backtrace]
            source: io::Error,
        },
    }
  • For variants that use #[from] and also contain a Backtrace field, a backtrace is captured from within the From impl.

    #[derive(Error, Debug)]
    pub enum MyError {
        Io {
            #[from]
            source: io::Error,
            backtrace: Backtrace,
        },
    }
  • Errors may use error(transparent) to forward the source and Display methods straight through to an underlying error without adding an additional message. This would be appropriate for enums that need an "anything else" variant.

    #[derive(Error, Debug)]
    pub enum MyError {
        ...
    
        #[error(transparent)]
        Other(#[from] anyhow::Error),  // source and Display delegate to anyhow::Error
    }

    Another use case is hiding implementation details of an error representation behind an opaque error type, so that the representation is able to evolve without breaking the crate's public API.

    // PublicError is public, but opaque and easy to keep compatible.
    #[derive(Error, Debug)]
    #[error(transparent)]
    pub struct PublicError(#[from] ErrorRepr);
    
    impl PublicError {
        // Accessors for anything we do want to expose publicly.
    }
    
    // Private and free to change across minor version of the crate.
    #[derive(Error, Debug)]
    enum ErrorRepr {
        ...
    }
  • See also the anyhow library for a convenient single error type to use in application code.


Comparison to anyhow

Use thiserror if you care about designing your own dedicated error type(s) so that the caller receives exactly the information that you choose in the event of failure. This most often applies to library-like code. Use Anyhow if you don't care what error type your functions return, you just want it to be easy. This is common in application-like code.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

thiserror's People

Contributors

aaron1011 avatar astraw avatar cosmicexplorer avatar de-vri-es avatar dtolnay avatar faern avatar jordens avatar kornelski avatar mathstuf avatar matklad avatar mina86 avatar ninevra avatar nyurik avatar ralfjung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

thiserror's Issues

Accept `.var` shorthand inside of parens

For example:

#[derive(Error, Debug)]
pub enum Error {
    #[error("{}", print(.0))]
    Io(io::Error),
}

fn print(error: &io::Error) -> String {
    unimplemented!()
}

We should expand .var immediately after (, not just ,.

Some kind of from attribute without Backtrace

I mean what's the point of Backtrace? If something crashes you can always use RUST_BACKTRACE=1 cargo run and see the entire backtrace.

Therefore it'd be nice to be able to derive a from impl, but without the requirement for a Backtrace field.

Generic type parameter on error

I ran into an error when having a generic parameter in my error type. So the setup is that I want to have a variant in my error enum such that it delegates to a different error, like below:

use thiserror::Error;

#[derive(Debug, Clone, Error)]
enum Error<E> {
    #[error("variant error")]
    Variant,
    #[error(transparent)]
    Delegate(#[from] E),
}

Unfortunately, I get an error about E not having a Debug instance:

error[E0277]: `E` doesn't implement `std::fmt::Debug`
 --> src/main.rs:4:1
  |
4 | enum Error<E> {
  | ^^^^^^^^^^ - help: consider restricting this bound: `E: std::fmt::Debug`
  | |
  | `E` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
  |
  = help: the trait `std::fmt::Debug` is not implemented for `E`
  = note: required because of the requirements on the impl of `std::fmt::Debug` for `Error<E>`

Could this be possible to do or is there something that is restricting this use case? 🤔

For now, I'm going to write out the implementations by hand, but it would be cool to delegate that work to thiserror.

Thanks for any help 🙏

Attribute for delegating Error impl to one field

Something to easily delegate the Error trait's source and backtrace methods and the Display impl to a field. This could be useful for hiding error variants from a library's public error type.

use thiserror::Error;

#[derive(Error, Debug)]
#[error(transparent)]  // <--
pub struct Error(ErrorKind);

#[derive(Error, Debug)]
enum ErrorKind {
    #[error("...")]
    E0,
    #[error("...")]
    E1(#[source] io::Error),
}

Document that #[error] attribute works for whole structs too

I just needed to derive primitive Display for a ZST and since I was already using thiserror I immediately started to search for solution. It's not documented, but I had a strong suspicion and found #3.

Please document it, so it's easier to find. A single phrase or an example would be sufficient.

Make generated code to be independent of `thiserror` crate... or maybe not

This is something that might be very specific to what we are doing in our codebase.

We currently use failure::Fail for deriving error boilerplate. The way we use it in our codebase is we re-export it from our own "error library API" (which provides few more bits that are typically used in our error handling).

So, our client code can then import everything through our API:

use our_errors::{Fail, SomeOtherThingy};

However, the way failure::Fail derive macro works, it generates references to failure crate, so we have to add it as a dependency to every crate that uses error handling (in addition to the dependency on our own error handling crate).

thiserror would have the same issue in our codebase: it uses this little thiserror::private::AsDynError;, which would require us to add thiserror dependency to all of our crates.

I'm not sure how useful that would be for thiserror in particular, but some procedural macros go extra mile to allow being abstracted away by other crates (for example inventory supports crate = xyz attribute for that purpose).

On one hand, thiserror is not really meant to be embedded in other crates, it doesn't look like this would be very useful. It's just us doing weird (and, maybe, wrong) things.

On the other hand, I feel that pretty much every procedural macro should be somehow abstracted away from its "runtime" crate, similar to how $crate works in macro_rules, just for the "hygiene" reasons, maybe (also, in case crate gets renamed, etc)?

However, maybe, it's not the job of the macros to provide that abstraction and instead the Rust procedural macros framework should have something similar to $crate attribute. Though, due to the dependency ordering, it's not quite clear how exactly that could work...? Maybe, by doing something like:

// re-export and bind `$crate` to `xyz` for this particular re-export of procedural macro. `self` could be used to indicate self crate -- which would work even if crate gets renamed via `Cargo.toml`.
#[crate = xyz]
pub use thiserror_impl::*;

Support mixing shorthand and non-shorthand format args

As in:

mod msg {
    pub const ERR_INVALID_UTF8: &str = "argument not convertible to UTF-8";
}

#[derive(Error, Debug)]
pub enum Error {
    #[error("{}: {value:?}", msg::ERR_INVALID_UTF8)]
    InvalidUtf8 { value: std::ffi::OsString },
}

Currently this fails because, as documented in the readme, our "{value:?}" shorthand does not kick in if any explicit format args are specified.

Feature request: context() and with_context() on Result and Option

I just switched from Snafu to thiserror, and on the whole my code has become a lot cleaner and clearer.

One thing that I miss is Snafu’s context() method on Result. It works much like the context() method in anyhow, but it allows adding structured context to an error. My current use case:

#[derive(Debug, Error)]
enum MyError {
    // . . .
    #[error("failed reading page {path:?}")]
    ReadPageFile { source: io::Error, path: PathBuf },
}

impl MyError {
    #[allow(non_snake_case)]
    fn ReadPageFileMap(path: &Path) -> Box<dyn FnOnce(io::Error) -> MyError> {
        let path = PathBuf::from(path);
        Box::new(|source: io::Error| MyError::ReadPageFile { source, path })
    }
}

fn read_from(path: &Path) -> Result<Page> {
    let raw = fs::read_to_string(path)
        .map_err(MyError::ReadPageFileMap(path))?;
    // . . .
}

Put more simply, I just want a convenient way to convert an io::Error into MyError::ReadPageFile while adding a path field to it.

#[source] when specific type is not known

My problem came up from trying to transform errors from .parse(). I tried something along the lines of

use thiserror::Error;                                               
#[derive(Debug, Error)]                                             
enum MyError {                                                      
    #[error("Failed to parse")]                                     
    ParseError,                                                     
}                                                                   
                                                                    
fn my_parse_function<F>(s: &str) -> Result<F, MyError>              
where                                                               
    F: std::str::FromStr,                                           
{                                                                   
    s.parse().map_err(|_| MyError::ParseError)                      
}                                                                   
fn main() {                                                         
    let i: Result<u64, MyError> = my_parse_function("1");           
    let s: Result<String, MyError> = my_parse_function("something");
}                                                                   

But I'd be happy to modify my variant ParseError so it can have the #[source] and therefore, I could modify my function my_parse_function like this

fn my_parse_function<F>(s: &str) -> Result<F, MyError>              
where                                                               
    F: std::str::FromStr,                                           
{                                                                   
    s.parse().map_err(|e| MyError::ParseError(e))                      
}  

But I can't figure out how to write that (because the error type is actually an associated type of FromStr, name Err, so the actual type is not specify). Is there some obvious solution I missed? It might be that what I'm trying to do is actually a flawed design (but it's not obvious to me at the moment). If it's not flawed and there is no obvious solution I missed, is this a use case likely to be supported in some way?

Option to `impl Error` under feature flag

Right now, I have an enum for various error types. Display is in core, so that's implemented unconditionally. However, Error only exists in std, so I've put it behind a feature flag (named "std") to allow compatibility with #![no_std]. Having the ability to do this programmatically would be helpful for this use case.

Something like this would be workable, I think.

#[derive(Error)]
#[error_feature = "std"]
enum Foo { ... }

as_dyn_error compilation error

I searched through the documentation for any reference to as_dyn_error but nothing came back.

use thiserror::Error;

mod msg {
    pub const ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8: &str =
        "Error: supplied command-line argument not convertible to UTF-8";
}

pub enum Error {
    #[error("{}: {:?}", msg::ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8, 0)]
    ArgNotConvertibleToUtf8(#[from] std::ffi::OsString),
}

yields:

error[E0599]: no method named `as_dyn_error` found for type `&std::ffi::OsString` in the current scope
 --> src/error.rs:6:17
  |
6 | #[derive(Debug, Error)]
  |                 ^^^^^ method not found in `&std::ffi::OsString`
  |
  = note: the method `as_dyn_error` exists but the following trait bounds were not satisfied:
          `&std::ffi::OsString : thiserror::aserror::AsDynError`
          `std::ffi::OsStr : thiserror::aserror::AsDynError`
          `std::ffi::OsString : thiserror::aserror::AsDynError`

error: could not compile `foo`.

yet commenting out the #[from] attribute and creating my own From impl:

impl From<std::ffi::OsString> for Error {
    fn from(err: OsString) -> Self {
        Self::ArgNotConvertibleToUtf8(err)
    }
}

and everything compiles fine. Am I missing something obvious?

Is there a way to do tuple unpacking?

Use case:

#[derive(Error)]
enum MyError {
    #[error("expected {} initializer, got {}",
        if *(.is_scalar) { ("scalar", "aggregate") } else { ("aggregate", "scalar") }
    )]
    ScalarAggregateMismatch { is_scalar: bool },
}
error: 2 positional arguments in format string, but there is 1 argument
   --> src/data/error.rs:138:23
    |
138 |     #[error("expected {} initializer, got {}",
    |                       ^^                  ^^
139 |         if *(.is_scalar) { ("scalar", "aggregate") } else { ("aggregate", "scalar") }
    |         -----------------------------------------------------------------------------

Best practice for "catch all" error?

I'd like to use a single Error type for various modules inside my crate, and occasionally the need arises to propagate errors from other projects using ? for ergonomics.

It seems intuitive to just include a

    #[error("Internal Error")]
    InternalError {
        #[from]
        e: anyhow::Error,
    },

in my Error enum, but the problem is that some errors require an additional conversion into anyhow::Error, for example:

fn load_records(db: DB) -> Result<Vec<Record>, MyError> {
    db.get_connection()?.load_records(...)?
}

In the above example I'd like the possible errors from diesel/r2d2/what not to map to my InternalError variant. The problem is there is no automatic conversion from those into MyError, since it requires an additional conversion to anyhow::Error...

What is the idiomatic/recommended way to deal with these cases?

Thanks in advance

Warn about double-inclusion of error message

I recently cooked up bytecodealliance/wasmtime#532 which fixed a "double error message" getting printed for errors such as:

pub enum CompileError {
    #[error("WebAssembly translation error: {0}")]
    Wasm(#[from] WasmError),
}

It'd be great if this crate could somehow issue a diagnostic indicating that there's no need to include the {0} in the #[error] because it's already rendered in the final error via #[from]

Optimize "{0}" and "{var}" to not require `write!`

#12 called out that the generated code from "{0}" is messier than just a Display::fmt(_0, formatter). I don't know why write! doesn't handle this case better, but we might as well do it in thiserror.

#[derive(Error, Debug)]
pub enum ParseFromEnvError {
    #[error("{0}")]
    InvalidLogFormat(io::Error),
}

^ Should expand the Display impl to:

  match self {
-     ParseFromEnvError::InvalidLogFormat(_0) => write!(formatter, "{}", _0),
+     ParseFromEnvError::InvalidLogFormat(_0) => Display::fmt(_0, formatter),
  }

@MOZGIII

Figure out what to do about void enums

We should have some way to have derive(Error) emit a Display impl for a void enum; I don't think it works currently. Specifically, for enums the macro looks for at least one variant that has a #[error("...")] attribute to decide whether to generate Display, and void enums don't have any variants.

#[derive(Debug, Error)]
pub enum Error {}

Maybe the following should generate a Display impl, but it seems a bit silly because the message is meaningless as the Display impl is unreachable.

#[derive(Debug, Error)]
#[error("...")]
pub enum Error {}

Reject attributes in unrecognized places

This should fail to compile, and point out that the attribute is not recognized there. Currently it silently ignores the attribute.

#[derive(Error, Debug)]
pub enum Error {
    Io(#[error("{}")] io::Error),
}

#[from] without implicit #[source]

TL;DR

It would be great to have an equivalent of #[from] attribute, which doesn't provide implicitly the functionality of #[source].

The setting

I've got an enum:

#[derive(Error)]
enum MyError {
    Failure(String),
}

and a function I didn't write:

fn fallible() -> Result<(), String> { ... }

which I want to use inside my function:

fn meta_fallible() -> Result<(), MyError> {
    ...
    fallible()?;
    ...
}

The problem

This won't compile and AFAIK there's no ergonomic way to make MyError smart enough to make it work.
Right now meta_fallible won't be able to convert error String to MyError, because it lacks right from. Let's try to add it:

#[derive(Error)]
enum MyError {
    Failure(#[from] String),
}

Now it won't compile, because while From<String> for MyError would be fine, String can't be used as &dyn std::error::Error, so it will fail as a thing returned from source. The only way to bypass this is manual implementation of From<String> for MyError.

Compilation error when using lifetime annotations with #[from]

Hi,

I’ve just run into an issue with thiserror. It may just be my inexperience with Rust’s lifetimes, though. The issue occurred when I was trying to use the #[from] attribute on an error that requires a lifetime specifier. I managed to create a minimal working example:

fn main() -> Result<(), Error<'static>> {
    Err(Error::SubError(SubError::Variant("some text")))
}

#[derive(Debug, thiserror::Error)]
enum Error<'a> {
    #[error("sub error")]
    SubError(#[from] SubError<'a>),
}

#[derive(Debug, thiserror::Error)]
enum SubError<'a> {
    #[error("variant: {0}")]
    Variant(&'a str),
}

This gives the following compilation error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:5:17
  |
5 | #[derive(Debug, thiserror::Error)]
  |                 ^^^^^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 6:12...
 --> src/main.rs:6:12
  |
6 | enum Error<'a> {
  |            ^^
note: ...so that the types are compatible
 --> src/main.rs:5:17
  |
5 | #[derive(Debug, thiserror::Error)]
  |                 ^^^^^^^^^^^^^^^^
  = note: expected  `&Error<'_>`
             found  `&Error<'a>`
  = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `SubError<'_>` will meet its required lifetime bounds
 --> src/main.rs:5:17
  |
5 | #[derive(Debug, thiserror::Error)]
  |                 ^^^^^^^^^^^^^^^^

error: aborting due to previous error

Any ideas as to how this could be solved?

Thanks for your work on anyhow and thiserror; IMHO they are the best the Rust ecosystem has to offer in terms of error handling :)

Supporting doc comment syntax as an alternative to `#[error]`

After taking a look at displaydoc (https://github.com/yaahc/displaydoc), which I believe is based upon code from thiserror, I'm somewhat enamored at its use of doc comment syntax rather than a custom attribute and I'm curious whether you'd consider allowing the same for thiserror (especially since you already go to lengths to allow users to not have to manually use the source and backtrace custom attributes).

Using the example in the documentation for comparison, the before:

use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("data store disconnected")]
    Disconnect(#[from] io::Error),
    #[error("the data for key `{0}` is not available")]
    Redaction(String),
    #[error("invalid header (expected {expected:?}, found {found:?})")]
    InvalidHeader {
        expected: String,
        found: String,
    },
    #[error("unknown data store error")]
    Unknown,
}

...and the after:

use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    /// data store disconnected
    Disconnect(#[from] io::Error),
    /// the data for key `{0}` is not available
    Redaction(String),
    /// invalid header (expected {expected:?}, found {found:?})
    InvalidHeader {
        expected: String,
        found: String,
    },
    /// unknown data store error
    Unknown,
}

Empty structures are not supported

Should this be valid?

#[derive(Debug, Error)]
struct EmptyStruct;

Or should this have a custom Display impl (if so, docs on it would be nice).

Empty enum seems fine since that is just Void. Though I guess None is fine for the implementation of the methods there…

Reject duplicate attributes

Multiple #[error("...")] should not be allowed.

#[derive(Error, Debug)]
#[error("one message")]
#[error("two message")] // compile_fail
pub struct Error;

Also multiple #[source].

#[derive(Error, Debug)]
pub struct Error {
    #[source]
    a: serde_json::Error,
    #[source] // compile_fail
    b: anyhow::Error,
}

1.0.8 broke my `error()` derive

I just upgraded from 1.0.4 to 1.0.8 and it broke my build. The following used to compile fine with thiserror:

use thiserror::Error;

#[derive(Debug)]
struct Inner {
    data: String
}

#[derive(Debug, Error)]
#[error("{}", .0.data)]
struct S(Inner);

fn main() {
    let s = S(Inner { data: "Hello, world!".to_string() });
    println!("{}", s);
}

With 1.0.8, I now get an error:

error[E0277]: `Inner` doesn't implement `std::fmt::Display`
 --> src/main.rs:9:16
  |
9 | #[error("{}", .0.data)]
  |                ^ `Inner` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `Inner`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: required by `std::fmt::Display::fmt`

error: aborting due to previous error

Is this intentional? If so, why? I don't need to print .0 to print the error message.

Some more things I tried:

  • #[error("{}", 0.data)]
error[E0308]: mismatched types
 --> src/main.rs:9:15
  |
9 | #[error("{}", 0.data)]
  |               ^
  |               |
  |               expected reference, found integer
  |               help: consider borrowing here: `&0`
  • #[error("{}", &0.data)]
error: expected expression, found `,`
 --> src/main.rs:8:17
  |
8 | #[derive(Debug, Error)]
  |                 ^^^^^ expected expression

error: proc-macro derive produced unparseable tokens
 --> src/main.rs:8:17
  |
8 | #[derive(Debug, Error)]
  |                 ^^^^^
  • #[error("{}", (.0).data)]: expected reference, found struct 'std::string::String'
  • #[error("{}", (&.0.data))] - error: float literals must have an integer part
  • #[error("{}", (&self.0.data))] - works but gives warning about unneccessary parentheses
  • #[error("{}", &self.0.data)] - error: proc-macro derive produced unparseable tokens

Parse .0 fmt arguments better

For example #[error("{}", .0.data)] currently works but #[error("{}", &.0.data)] does not. We should make the .0 / .var variables work wherever possible.

Generate From impls

Error types commonly provide From impls so that they can be constructed by ?.

I don't want to become a fully general From derive, but I would be okay with providing a convenient From impl just for sources. Any From impl other than from the source should be handwritten for visibility or using something like derive_more.

#[derive(Error, Debug)]
enum EnumError {
    Example(#[from] io::Error),
}

the trait `std::convert::From<std::convert::Infallible>` is not implemented for `MyOwnError`

I'm generating code which can result in this error:

the trait `std::convert::From<std::convert::Infallible>` is not implemented for `MyOwnError`

This is because (of course) I have a function which can't fail (from_str() actually) but try to convert that to a Result<_, MyOwnError> via a question mark.
For now I have implemented this workaround manually:

impl From<Infallible> for MyOwnError {
    fn from(_: Infallible) -> Self {
        unreachable!()
    }
}

but I'd prefer if it could be done automagically.

error(transparent) on structures

Hello,

I think I haven't had yet the occasion to thank you for thiserror (as well as a lot of other crates actually), so here it is: thank you! And sorry for not having had issues with your crates earlier, which would have allowed me to thank you without just spamming 😛

I recently hit a limitation of thiserror: sometimes, I just want to hide an error type from a sub-library, and re-export it under another name, so that the implementation details don't leak.

So I originally tried this:

#[derive(Debug, thiserror::Error)]
struct E(#[error(transparent)] std::io::Error);

However, it looks like this doesn't implement Display for E.

On the other hand, this works perfectly:

#[derive(Debug, thiserror::Error)]
enum E { #[error(transparent)] Foo(std::io::Error) }

But then, it leaks the implementation detail of the inner error type.

Do you think it'd make sense to have #[error(transparent)] work on structs? Or maybe it'd be a bad idea anyway for reasons I can't think of right now?

Is it possible (or intended) to use an enum member as a source?

It isn't possible to use an existing enum member as a #[source], in situations where you want to accumulate or transform errors that have been defined in the same crate, so you can drill down using source():

use thiserror::Error;

#[derive(Error, Debug)]
pub enum SomeError {
    #[error("Foo")]
    FooError,
    #[error("Bar")]
    Test(#[source] crate::SomeError)
}

As this leads to error[E0072]: recursive type SomeError has infinite size. It can be fixed by boxing crate::SomeError, but is this a terrible idea / an intended limitation / am I missing something obvious?

Support using error message from source

I have a very simple enum that just wraps all other error types:

#[derive(Debug)]
pub enum SomeParseFromEnvError {
    InvalidLogFormat(ParseFromEnvError<InvalidLogFormat>),
    InvalidBool(ParseFromEnvError<ParseBoolError>),
    InvalidInfallible(ParseFromEnvError<Infallible>),
}

The Display implementation I have doesn't do anything fancy - it just relies the fmt directly to the wrapped value:

impl std::fmt::Display for SomeParseFromEnvError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SomeParseFromEnvError::InvalidLogFormat(err) => err.fmt(f),
            SomeParseFromEnvError::InvalidBool(err) => err.fmt(f),
            SomeParseFromEnvError::InvalidInfallible(err) => err.fmt(f),
        }
    }
}

I need a simple way to derive the same behavior.
Annotation every field with #[error("{0})] is an option that I currently have to use, but it doesn't expand to the same code I write manually.
Also, #[error("{0})] above every field doesn't look good 😄

Accept top level #[error(...)] attribute on enums to apply to all variants

use thiserror::Error;

#[derive(Error, Debug)]
#[error("{0}")]
pub enum Error {
    Io(io::Error),
    Json(serde_json::Error),
    Regex(regex::Error),
}

^ this should be treated as equivalent to:

#[derive(Error, Debug)]
pub enum Error {
    #[error("{0}")]
    Io(io::Error),
    #[error("{0}")]
    Json(serde_json::Error),
    #[error("{0}")]
    Regex(regex::Error),
}

Add no_std support

It would be great to have no_std support for thiserror, and after a look over the codebase I don't think it would be very difficult, I'm just inexperienced with proc-macros. The only drawbacks (and thus make no_std compatibility require a feature flag) would be that std::error::Error and std::path are not core or alloc friendly. For the most part, migration would be easy as changing std imports to core or alloc, but for the aforementioned roadblocks I propose the following:

Add #![cfg_attr(feature = "no_std", no_std)] to the root of the thiserror crate to enable no_std for the no_std feature
Add a feature to both thiserror and thiserror-impl, possibly something like no_std, this would look about like this

# In thiserror
[features]
default = []
no_std = ["thiserror-impl/no_std"]

# In thiserror-impl
[features]
default = []
no_std = []

And then for problematic areas, like thiserror/src/aserror.rs and thiserror/src/display.rs, simply conditionally compile them when the no_std feature is not active using #[cfg(not(feature = "no_std))]
Lastly there'd be the normal changing std to core/alloc, but adding conditional compilation to places where aserror.rs and display.rs are used would also be needed

Unhelpful error message if the error attribute and fmt::Display impl are missing

Problem

This code

use thiserror::Error;

#[derive(Debug, Clone, Error, PartialEq)]
pub enum Error {
    First,
    Second,
}

produces this compiler error

error[E0277]: `Error` doesn't implement `std::fmt::Display`
 --> src/lib.rs:3:24
  |
3 | #[derive(Debug, Clone, Error, PartialEq)]
  |                        ^^^^^ `Error` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `Error`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `terr`.

To learn more, run the command again with --verbose.

It is not immediately obvious that you have to add attributes to the enum variants.

use thiserror::Error;

#[derive(Debug, Clone, Error, PartialEq)]
pub enum Error {
   #[error("first")]
    First,
    #[error("second")]
    Second,
}

Suggestion

A better error message would be something like this

error[E0277]: `Error` doesn't implement `std::fmt::Display`
 --> src/lib.rs:4:10
  |
4 | pub enum Error {
  |          ^^^^^ `Error` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `Error`
  = note: use the `#[error("...")]` attribute on variants to derive `std::fmt::Display`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `terr`.

To learn more, run the command again with --verbose.

Implementation

The error happens, because this if clause

let display_impl = if input.has_display() {

in

fn impl_enum(input: Enum) -> TokenStream {

returns None, if input.has_display() is false (to support structs, which implement std::fmt::Display manually?).

I know that the suggested error message is not possible (or is it?), because proc-macros operate on tokens and can therefore not know if a type implements std::fmt::Display.


It is still possible to improve the error message (a little) by returning this in the else clause:

Some(quote_spanned! {
    ty.span() => struct _AssertDisplay where #ty: ::core::fmt::Display;
})

which should result in this compiler error

error[E0277]: `Error` doesn't implement `std::fmt::Display`
 --> src/lib.rs:4:10
  |
4 | pub enum Error {
  |          ^^^^^ `Error` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `Error`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `terr`.

To learn more, run the command again with --verbose.

RFC: `From` for boxed errors

In TiKV we often have very large error types (mostly due to including protobuf data with errors) and these errors appear on hot paths (because we try to recover from them). We found that boxing our errors improves our performance by a non-trivial amount.

A small tweak that makes our error handling much more ergonomic is to generate From impls from the unboxed type. Since we often convert error types from external crates into boxed errors, this lets us use a simple ?, rather than mapping the error.

E.g.,

#[derive(Error, Debug)]
pub enum Error {
    #[error(transparent)]
    ProtoErr(#[from] Box<protobuf::Error>),
    #[error(transparent)]
    Other(anyhow::Error),
}

impl From<protobuf::Error> for Error {
    fn from(e: protobuf::Error) -> Error {
        Error::ProtoErr(Box::new(e))
    }
}

I suppose that for backwards compatibility such a thing should be opt-in, e.g., using #[from(unbox)] or using a Cargo feature.

I have a prototype implementation which was straightforward (though I didn't implement any kind of opt-in).

Print all source errors for alternate format `{:#}`

Would it be possible to have the alternate format, {:#}, print all the source errors, like anyhow::Error does?

When logging errors, we want to include the full stack of errors, which means when logging anyhow errors, we use either alternate or debug format, and when using thiserror errors, we use display because we've manually included the source error in the display string. This means the author of each error log statement needs to be aware of this and remember what type of error is being logged. It also leads to the source error of thiserror being logged twice if it is e.g. wrapped in an anyhow::Context.

Thank you, these libraries are very nice.

Support custom string for None/Some(T)

It would be very useful to be able to support specialized display strings for when an argument of an enum is of type Option<T> and is the variant None. This would also remove the Some(...) from the display on the Some variant.

Intermittent compile failures using field interpolation

I'm getting puzzling behavior when using field interpolation:

mod msg {
    pub const ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8: &str =
        "Error: supplied command-line argument not convertible to UTF-8";
}

use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
pub enum Error {
    #[error("{}: {value:?}", msg::ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8)]
    ArgNotConvertibleToUtf8 { value: std::ffi::OsString },
}

yields:

error: there is no argument named `value`
  --> src/error.rs:15:18
   |
15 |     #[error("{}: {value:?}", msg::ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8)]
   |                  ^^^^^^^^^

But simply by changing the #[error()] attribute to:

    #[error("{}: {:?}", msg::ERR_ARG_NOT_CONVERTIBLE_TO_UTF_8, value)]

everything works as expected.

Then I discovered something really puzzling: what's "intermittent" about this issue is that when trying to isolate it, I discovered that:

pub enum MyError {
    #[error("uh oh: {value:?}")]
    Whoops { value: std::ffi::OsString },
}

works perfectly fine.

To my eye, these are the same use case! Any ideas why the ArgNotConvertibleToUtf8 variant's interpolated #[error()] attribute is problematic?

Great crate, by the way. I love the design principle that thiserror does not appear in my library's API.

enum when no field is enabled it will trigger: trait `std::fmt::Display` is not implemented...

When there is no feature enabled in the build

#[derive(Debug, Error)]
pub enum PlatformError {
    #[cfg(feature = "with-postgres")]
    #[error("{0}")]
    PostgresError(#[from] PostgresError),
    #[cfg(feature = "with-sqlite")]
    #[error("{0}")]
    SqliteError(#[from] SqliteError),
    #[cfg(feature = "with-mysql")]
    #[error("{0}")]
    MysqlError(#[from] MysqlError),

The compiler will spit out this error.

error[E0277]: `error::PlatformError` doesn't implement `std::fmt::Display`
  --> src/error.rs:37:17
   |
37 | #[derive(Debug, Error)]
   |                 ^^^^^ `error::PlatformError` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `error::PlatformError`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

To get around this, I need to put a variant that is always there even when there is no feature is selected during the build.

#[derive(Debug, Error)]
pub enum PlatformError {
    #[cfg(feature = "with-postgres")]
    #[error("{0}")]
    PostgresError(#[from] PostgresError),
    #[cfg(feature = "with-sqlite")]
    #[error("{0}")]
    SqliteError(#[from] SqliteError),
    #[cfg(feature = "with-mysql")]
    #[error("{0}")]
    MysqlError(#[from] MysqlError),
    /// FIXME: this is a placeholder error
    /// such that thiserror::Error will not complain
    /// for fmt::Display not implemented, when no feature is enabled
    #[error("{0}")]
    Other(String),
}

Integration test cases

I work on rcc, a c compiler in rust. Thiserror has been very valuable in the development of the rcc. Over time we have noticed some very minor issues with the macro and I was wondering if you would be interested in using our error types as an integration test. The src/data/error.rs file in the rcc repository contains large error types using the thiserror derive macro.

Add factories in addition to `From` impls

Hi, first of all thanks a lot for this crate.

This afternoon I've been trying to migrate my sandbox project from failure to thiserror and realized I would really appreciate if I had some kind of factories in addition to From impls. Here is my proof of concept:

https://github.com/arnodb/thiserror/tree/factory_impls

And the use I am making of it:

https://github.com/arnodb/torus_trooper_rs/tree/thiserror-migration

That eases the abstraction of the optional backtrace member. The macro implementation is probably not optimal. There may also be missing stuff, corner cases etc. This is also related to #57 because, as you can see, I am using a pattern like:

res
    .map_err(Box::from)
    .map_err(MyError::my_factory)?

Which is, so far, the best I found to avoid res.map_err(|err| MyError::my_factory(Box::new(err))).

I would like your opinion about this work. If you are interested in getting this mainstream then I am open to refinement suggestions.

backtrace() should prefer the source's backtrace if it has one

#[derive(Error, Debug)]
pub struct Error {
    #[source]
    source: anyhow::Error,
    backtrace: Backtrace,
}

Currently the generated backtrace() method would just return Some(&self.backtrace). Instead it should be returning Some(self.source.backtrace().unwrap_or(&self.backtrace)). The lower-level error will tend to have a deeper more relevant backtrace.

Note: I am not confident about this. Which behavior is the right one?

Separately, we should return source backtraces if self doesn't contain its own backtrace, which we don't currently do. self.source.backtrace() in the following case.

#[derive(Error, Debug)]
pub struct Error {
    #[source]
    source: anyhow::Error,
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.