GithubHelp home page GithubHelp logo

rust-lang-deprecated / failure Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 140.0 1.32 MB

Error management

Home Page: https://rust-lang-nursery.github.io/failure/

License: Apache License 2.0

Rust 98.60% Shell 1.06% Makefile 0.34%
error-handling rust

failure's People

Contributors

cad97 avatar colelawrence avatar davidbarsky avatar durka avatar eijebong avatar exphp avatar hcpl avatar jaroslaw-weber avatar jrobsonchase avatar juggle-tux avatar kasma1990 avatar keeperofdakeys avatar king6cong avatar mark-i-m avatar matklad avatar mitsuhiko avatar mre avatar mystor avatar noskcaj19 avatar o01eg avatar palfrey avatar phrohdoh avatar qnighy avatar roughsketch avatar rustonaut avatar scurest avatar steveklabnik avatar technetos avatar the-kenny avatar withoutboats 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

failure's Issues

Cannot lazily construct `Context`

Error chain's chain_err takes a Fn so it will only be evaluated in the error case.

    fn process_collection() -> Result<()> {
        let path = "non-existent-file";
        process_file(path).chain_err(|| format!("Processing {}", path))
    }

Unless I've misread things, Context can't. It takes a Display

    fn process_collection() -> Result<(), Error> {
        let path = "non-existent-file";
        process_file(path).context(format!("Processing {}", path))?;
        Ok(())
    }

Another use case where we might want for it to be lazy: .clone()-ing the object passed to it.

Let's bikeshed on names!

I like the general design of this crate a lot. I think that Fail is a solid improvement over the existing Error trait, and that the proposed Error struct is a good default mechanism for error handling (though not a universal approach, since it relies on dynamic memory allocation, we'll probably continue to need mechanisms for generating a One True Error Type for some use cases).

That being said, I would prefer for my error handling code not to be riddled with internet meme references, so it would be nice if there were a more "english" name like Failure for the trait that error types need to implement.

And on a related note, there is the question of whether we could find less synonymous names than "Error" and "Fail(ure)" in order to more easily discriminate which of the two is a trait and which is a concrete type. This is especially true since if this crate gets popular, we would enter a transition period where people's crates manipulate two kinds of Error, one of which is a trait (std::error::Error) and one of which is a struct (failure::Error). This could get confusing quickly.

With these two considerations in mind, how about naming the trait Error and the struct AnyError? Error is consistent with the std naming convention, and allows for a smooth migration if the trait is eventually merged into the standard library, whereas AnyError should hint quite well at the fact this this is a type that can hold any error (with a subtle hint at the Any trait for type erasure).

Allow `Context` to wrap `Fail` implementations

Moving this from reddit.

Context can either be a cheaply constructed Fail or an extension to Error. It doesn't allow wrapping a struct that implements Fail.

This was more understandable when it felt like the focus was exclusively on using Error but now with the emphasis being on there being multiple workflows, this seems like shortcoming in the API.

Small feedback: the `context` method in `ResultExt`is not a good name.

For example, it can be very hard for someone to understand the meaning of context when reading the following line (specially a person not very familiar with the failure crate):

let mut file = File::open(cargo_toml_path).context("Missing Cargo.toml")?;

Something like fail_context would probably be more appropriate and communicate the intent better.

Idea: Automatically add context-field when converting an error

It could be great to use a decorator to automatically add one or more fields when converting an error to another error. Perhaps it could look like this

#[derive(Fail, Debug)]
struct ParseError {
    filename: String, 
    error: MyError
}

// do_stuff returns a MyError, but the add_fields decorator causes the error to be 
// converted in to a ParseError adding a filename.to_owned() as a field.
#[fail(add_fields = [filename])]
fn read_file(filename: &str) -> Result<(), ParseError>{
    do_stuff(filename)?;
    do_stuff(filename)?;
    Ok(())
}

I am not sure if the above is actually doable, and the syntax probably needs tweaking, but it could be really useful to have something to this effect.

std::option::NoneError, what to do?

Hi, I scoured the docs and had a bit of a hard time discerning if Failure should handle the NoneError type that can be returned when a you call to_str() on a PathBuf, I'm migrating my code from error_chain and I used the .chain_error() method to convert these errors into compatible types. Am I missing something or should I implement From fro NoneError?

A method for iterating over causes

Currently there is cause and root_cause.

It'd be nice to have something like iter_causes to help in cases like this

println!("Error: {}", err);
let mut cause = err.cause();
while let Some(c) = cause {
    println!("Caused by: {}", c);
    cause = c.cause();
}

to

println!("Error: {}", err);
for cause in err.iter_causes() {
    println!("Caused by: {}", cause);
}

Export proc-macros directly from main crate

There's a trick that can export traits and proc macros from one crate. Using this would make the dependency declaration cleaner, and could be gated on a default feature for people who don't need the macros.

Clarify lack of `backtrace` and `cause` on custom fail types

Maybe its from having come from error-chain where too much magic is happening but I feel the documentation obscures the fact that custom Fails will not have a backtrace or cause.

Two simple ways to improve this

  • In "Deriving Fail", rename "Overriding backtrace" and "Overriding cause" to "Including a backtrace" and "Including a cause".
    • While "overriding" is technically correct, it assumes knowledge of the library.
  • In "A Custom Fail Type" provide include commentary about backtrace and cause

Cannot impl trait for Error when it is implemented for T: Fail

Please take a look at example code. I have no idea why it is not possible to implement my trait for failure::Error and for T where T: Fail. As far as I saw the source, failure::Error does not implement Fail.

Code sample with error:

#[macro_use]
extern crate failure_derive;

trait FailMethods {
   fn foo(&self);
}

impl FailMethods for ::failure::Fail {
   fn foo(&self) {
       println!("foo");
   }
}

impl<T> FailMethods for T where T: ::failure::Fail {
   fn foo(&self) {
       (self as &::failure::Fail).foo();
   }
}

// impl FailMethods for ::failure::Error {
//     fn foo(&self) {
//         self.cause().foo();
//     }
// }
/*
 error[E0119]: conflicting implementations of trait `FailMethods` for type `failure::Error`:
   --> src/main.rs:21:1
    |
 15 | / impl<T> FailMethods for T where T: ::failure::Fail {
 16 | |     fn foo(&self) {
 17 | |         (self as &::failure::Fail).foo();
 18 | |     }
 19 | | }
    | |_- first implementation here
 20 |
 21 | / impl FailMethods for ::failure::Error {
 22 | |     fn foo(&self) {
 23 | |         self.cause().foo();
 24 | |     }
 25 | | }
    | |_^ conflicting implementation for `failure::Error`

 error: aborting due to previous error
*/

#[derive(Debug, Fail)]
#[fail(display = "my error")]
struct MyError {}

fn main() {
 let failure_error = ::failure::err_msg("dummy error");
 let custom_derived = MyError {};


 custom_derived.foo();
 failure_error.cause().foo();

 //failure_error.foo();
 /* error[E0599]: no method named `foo` found for type `failure::Error` in the current scope
      --> src/main.rs:37:17
       |
    37 |   failure_error.foo();
       |                 ^^^
       |
       = note: the method `foo` exists but the following trait bounds were not satisfied:
               `failure::Error : failure::Fail`
       = help: items from traits can only be used if the trait is implemented and in scope
       = note: the following trait defines an item `foo`, perhaps you need to implement it:
               candidate #1: `FailMethods`

    error: aborting due to previous error
 */
}

Why Context isn't generic over the error?

If context accepts a generic displayable thing as a context I see no inherent cost at adding another generic for error too, i.e. Context<D: Display, E: Fail>. Or is it somehow related to how question-mark/carrier work?

Tracking issue for failure 0.1.1

Hoping to release some time this week.

  • Merge #54 (Causes Iterator)
  • Merge #74 (bail! and ensure!)
  • Expose derive from failure directly (#71)
  • Make derive no_std compatible.
  • Add From<D> for Context<D> (#75)
  • Add a change log or release notes or something
  • Make a blog post

How many error types should libraries have in their APIs?

Should libraries have 1 error type in their API, or many? error-chain today strongly encourages having a single error type, which is probably not always the best approach.

The primary reason to have multiple error types is that you can then easily restrict a function to throwing only 1 particular error type. The primary disadvantage is that it becomes harder to deal with cases where there are multiple error types.

For example, in the error-chain docs, an example is shown with four variants:

  • Invalid Toolchain Name
  • Invalid Toolchain Version
  • IO Error
  • TOML Error

Notably, when parsing a toolchain identifier from a string, only the first two variants could occur. It would make sense, then, to have a situation like this:

#[derive(Debug, Fail)]
enum ToolchainError {
    InvalidName,
    InvalidVersion,
}

impl FromStr for ToolchainId {
     type Err = ToolchainError;
}

But this raises the question: should the larger section of code, which reads/parses a file containing toolchain ids in a toml document, have its own new error type, adding the other variants, or should it just return Error?

The questions posed then are:

  • How granular should errors be? Per crate, per module, per function? How do you know when you want to section off a new error type?
  • Once you've got multiple, granular errors, should you have agglutinative errors that merge all of the granular errors together, or should you just fall back to the Error type?

Accessing structs in in derived display appears to cause a failure.

I am currently writing a parser for fun, and I decided to give the failure crate a go.

In my attempts to try and provide as much information as possible in errors, I want to pass the offending token in my struct, and get information out of that token.

Here is the offending code:

#[derive(Debug, Fail)]
pub enum ParseError {
    #[fail(display = "Expecting a token to exist, but no token was found.")]
    NoTokenFound,
    #[fail(display = "{}: Expected a token of type {:?}, but got a token of type {:?}.", location,
       expected, got.ttype)]
    UnexpectedToken {
        location: Location,
        got: Token,
        expected: TokenType,
    },
}

When I change the reference to got.ttype to simply "got", it works fine.

Could we enable accessing struct fields, or is there a reason we must avoid using it?

Documentation Conventions

I've noticed that the documentations for this crate uses the convention `Fail`ure in place of the word failure. I'm finding this a bit hard to read, so I was wondering if you'd accept a PR to change it to just failure in the appropriate places.

Docs are missing an example on how to return a custom (derived) error

This is probably a simple issue, but I could not figure it out and I suppose others will also stumble upon it. It would be good if you could include a working example in the documentation for the situation described below:

I have my own error type which derives from Fail. It is unclear to me how it can be automatically converted to a failure::Error.

Minimal example:

Cargo.toml:

[dependencies]
failure = "0.1.0"
failure_derive = "0.1.0"

lib.rs:

extern crate failure;

#[macro_use] extern crate failure_derive;

use failure::{Error};

#[derive(Debug,Fail)]
#[fail(display = "This is my error and mine alone!")]
pub struct MyError;

pub struct Foo;

fn foo() -> Result<Foo,Error> {
    Err( MyError{} )
}

The error I receive when I am compiling this example:

error[E0308]: mismatched types
  --> src/lib.rs:15:10
   |
15 |     Err( MyError{} )
   |          ^^^^^^^^^ expected struct `failure::Error`, found struct `MyError`
   |
   = note: expected type `failure::Error`
              found type `MyError`

Thank you so much for this crate! This is already a lot nicer than error-chain.

Not clear how to handle tuple-structs

#[derive(Fail)]
enum MyError {
  #[fail(display = "The unthinkable happened: {}", 0)]
  SomeProblem(SomeData);
}

It's not clear how to provide a fail(display) attribute for this kind of error. Trying the above, I get an error that non-string literals in attributes are experimental (issue #34981).

Test the no_std mode

It would be great to add to .travis.yml an additional test run which does cargo test --no-default-features (turning off the std feature) on stable. The 4 existing test runs should still use std.

Not sure how to do this, but I'm sure its possible if someone reads the Travis docs. :)

"Compile" error in ErrorKind documentation

#[derive(Debug)]
struct MyError {
    inner: Context<MyErrorKind>,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
struct MyErrorKind {
    // A plain enum with no data in any of its variants
    //
    // For example:
    #[fail(display = "A contextual error message.")]
    OneVariant,
    // ...
}

struct MyErrorKind should be enum MyErrorKind.

Stable Error IDs and Error Formatting

I have been toying around with rust error handling for a long time now and I love where this crate here is going, but it misses out on one part that keeps frustrating me a bit.

There is no good way to identify a unique error unless you have the type. This makes sense but is very restrictive. What if instead an failure would have a UUID. The default can be generated from an md5 hash of the fully qualified name (UUID3) but alternatively users can manually implement it.

This can then be used for localization easily (#12):

impl Fail for Whatever {
    const ID: [u8; 16] = b"!\x06?"\xe6\x12F\x0f\x92t\xfe\xaa\x93\xf9<L";
}

The localization can then key the strings to that ID. Likewise this lets you send failures over the wire easily.

To make the error formatting better the messages however would have to be split up into more parts. In particular I am thinking of:

  • error name: the human readable name of the error (io error, network error etc.)
  • error description: a short description of what the error is (current Display maybe?)
  • error detail: additional error details (most likely will always be untranslated)

The idea would be that name + description are reasonably static strings that can be looked up from a translation table.

Context when using custom Fail enum

I just finished reading the book. I liked the "Patterns & Guidance" section, but something is still a bit unclear to me.

In the library I'm currently writing, I'd like to use a custom error enum containing all possible error cases. When using error_chain, I added a msg: String field as data to almost all the variants, in order to be able to add some context. For this library, this would translate to

#[derive(Fail, Debug)]
pub enum MyError {
    #[fail(display = "Parsing error: {}", _0)]
    Parsing(String),
    #[fail(display = "Overflow: {}", _0)]
    Overflow(String),
}

// ...

return Err(MyError::Parsing(format!("Could not parse URL \"{}\", url)));

This way I can simply return a Result<_, MyError> from my public library functions.

From what I understood when reading the failure docs, the recommended way to deal with this is the context though. So I tried to adapt the enum:

#[derive(Fail, Debug)]
pub enum MyError {
    #[fail(display = "Parsing error")]
    Parsing,
    #[fail(display = "Overflow")]
    Overflow,
}

// ...

return Err(MyError::Parsing).context(format!("Could not parse URL \"{}\", url)));

Now the return type of my functions can't be Result<_, MyError> though anymore, since the .context() method converts the error type to a Context. This means that I'd have to return Result<_, failure::Error> instead, right? That prevents the user from easily enumerating the errors though.

What is the suggested pattern in this case? Going back to the "embedded" context in the error types?

Debug and Display impls for provided types

I definitely just threw together the Debug and Display impls in this library for types like Error, Context, etc. They should be reviewed for quality and better ways of conveying the same information should be considered and discussed.

Helper for displaying all causes in the entire error chain

Thank you for digging into this tricky design problem!

I miss the "chained" error display provided by quick_main! in error-chain. A lot of my software has been designed around showing the various nested error messages. For example:

error reading "avatar_01_01.es.srt2": No such file or directory (os error 2)

Showing the full context of the error provides helpful clues to the user. But failure provides no easy way to get that string, and it seems to encourage the use of errors like:

error reading "avatar_01_01.es.srt2"

This is less ergonomic for users.

One workaround is to write something like this:

    // Decide which command to run, and run it, and print any errors.
    if let Err(err) = run(&args) {
        let mut opt_cause = Some(err.cause());
        let mut first = true;
        while let Some(cause) = opt_cause {
            if first {
                first = false;
            } else {
                write!(io::stderr(), ": ")
                    .expect("unable to write error to stderr");
            }
            write!(io::stderr(), "{}", cause)
                .expect("unable to write error to stderr");
            opt_cause = cause.cause();
        }
        write!(io::stderr(), "\n").expect("unable to write error to stderr");
        process::exit(1);
    }

This is potentially related to #41.

I don't think it's necessary to go as far as quick_main!. It might be enough to have a display_with_causes method or even a DisplayWithCauses newtype, allowing us to write something like:

write!(io::stderr(), "{}\n", err.display_with_causes())

Obviously some of this could be provided by an external crate. But there might be some nice ergonomic improvements to be made here.

Derive From?

Sorry if this is addressed but I didn’t immediately see how From<SomeError> works for an error that wraps another error, e.g. io::Error

Are we currently expected to implement from conversion for error types wrapping other errors ourselves?

Will errors with lifetimes be supported?

E.g., this fails to compile:

#[derive(Fail, Debug)]
pub enum LinkError<'a> {
    #[fail(display = "Undeclared symbolic reference to: {}", _0)]
    Undeclared (&'a str),
}

Are there plans to ever support this, or is this not possible due to other constraints?

Inconsistent behavior between `Error` and a `Fail` struct.

In some ways, an Error acts as a proxy for its inner Fail. In other ways, it doesn't. This seems like it could lead to surprising results when swapping one out for the other.

Fail

  • cause: child item
  • causes: starts with self
  • root_cause: includes self
  • downcast: acts on self

Error

  • cause: inner
  • causes: starts with inner
  • root_cause includes inner
  • downcast: acts on inner

In comparing them, the discrepancy is in cause.

I noticed this when reading the documentation for Error::causes.

Size of failure::Error is too big

extern crate failure;

fn main() {
    println!("{}", std::mem::size_of::<failure::Error>());
}

Currently 2 words but I would expect this to be 1 word. In my experience a return type like Result<(), Error> can be faster if 1 word than 2 words. (Let me know if this is controversial and I can try to recreate some benchmarks.)

Would it be possible to double-Box or use a C++like layout for the DST so Error can be a thin pointer?

Contradictory code in readme

In the code example in README.md:

// All we do is derive Fail and Display for it, no other "magic."
#[derive(Debug, Fail)]
enum ToolchainError {

Should the comment say "Fail and Debug" or should it be #[derive(Display, Fail)]?

`with_context`: expected concrete lifetime, found bound lifetime parameter

I'm converting the next branch of https://github.com/faradayio/bigml-rs from error-chain to failure, and I ran into an interesting lifetime issue using with_context.

In practice, this library really needs to return detailed context for errors: a message like "404 Not Found" provides totally insufficient debugging help, and we have a policy of wrapping these errors in a context containing the URL that failed.

But since catch isn't available on stable Rust, we tend to do this by declaring a local mkerr closure, and passing it to chain_err like chain_err(&mkerr). Below, I've tried to convert this code to work with failure:

    /// Create a new resource.
    pub fn create<Args>(&self, args: &Args) -> Result<Args::Resource>
        where Args: resource::Args
    {
        let url = self.url(Args::Resource::create_path());
        debug!("POST {} {:#?}", Args::Resource::create_path(), &serde_json::to_string(args));
        let mkerr = |_| CouldNotAccessUrl::new(&url);
        let client = reqwest::Client::new();
        let res = client.post(url.clone())
            .json(args)
            .send()
            .with_context(&mkerr)?;
        Ok(self.handle_response(res).with_context(&mkerr)?)
    }

This gives me the compiler error:

error[E0271]: type mismatch resolving `for<'r> <&[closure@src/client.rs:61:21: 61:53 url:_] as std::ops::FnOnce<(&'r reqwest::Error,)>>::Output == _`
  --> src/client.rs:66:14
   |
66 |             .with_context(&mkerr)?;
   |              ^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#131r

error[E0281]: type mismatch: `[closure@src/client.rs:61:21: 61:53 url:_]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r reqwest::Error,)>` is required
  --> src/client.rs:66:14
   |
61 |         let mkerr = |_| CouldNotAccessUrl::new(&url);
   |                     -------------------------------- implements `std::ops::Fn<(_,)>`
...
66 |             .with_context(&mkerr)?;
   |              ^^^^^^^^^^^^
   |              |
   |              expected concrete lifetime, found bound lifetime parameter
   |              requires `for<'r> std::ops::Fn<(&'r reqwest::Error,)>`
   |
   = note: required because of the requirements on the impl of `for<'r> std::ops::FnOnce<(&'r reqwest::Error,)>` for `&[closure@src/client.rs:61:21: 61:53 url:_]`

Now, I can work around this trivially. Among many other solutions, I can just write:

 .with_context(|_| CouldNotAccessUrl::new(&url))?

...which works perfectly fine. Indeed, if catch were stable, this would be 100% acceptable. But given the current constraints, I wonder if there isn't some tweak to the type of with_context that might allow my code to work?

impl<Inner> From<Inner> for Context<Inner>

I am personally trying out now a light variant of the custom Error + ErrorKind approach, where I have an ErrorKind but instead of a custom Error I just use type MyError = Context<MyErrorKind>;.

This works great for the usual from impl's but it doesn't help when I want to return try!(Err(ErrorKind::SomeKind)) directly.

Therefore I think it would be great to always have From<Inner> implemented for Context<Inner>. Right now I have to do try!(Err(Context::new(ErrorKind::SomeKind))) instead, or impl the from myself, but neither are nice things to do :).

impl Fail for NoneError?

Using the currently (but probably not for very much longer) unstable Try trait, one can use the question mark operator on Option<T> types, coercing them into a Result<T, NoneError>. Unfortunately, NoneError does not impl std::error::Error, meaning the Fail is not implemented for NoneError (and it being an external type, we can't implement it in our own crates).

I think this would be particularly useful in light of the pattern described in “Using the Error type” — throwing a NoneError fits right into the kind of rapid prototyping, "just show the user the error and exit" limited error handling that that exemplifies.

Would a PR with this change go amiss?

Should we recommend middle-layer libraries add context to errors?

For "leaf" libraries and "root" applications the story from failure is pretty clear - leaf libraries should define new types that implement Fail, and applications should use the Error type (unless they are no_std applications).

But what about the in-between space? What if you depend on a bunch of libraries with their own error types, and introduce a few of your own as well?

There are a few options I know about:

  1. Just return Error. In many cases, this is very convenient. The downsides are that you require the Error type (making it inappropriate to call these functions when failure could be in the hot path) and that users get no additional context about what the error means in the context of your library - libfoobar threw an io::Error, what does that mean?
  2. Wrap context around the underlying error. Importantly, this contextual information can have a many-to-many relationship: an underlying error could have many different semantic meanings about "what has gone wrong," whereas many different kinds of underlying errors could mean the same thing at this level of semantics. This requires manual application of context when the error occurrs (and can't be supported by a standard ? conversion), and when users are trying to downcast they now have to deal with all of this additional context information.
  3. Define a new error type with From conversions. This is what error-chain heavily encourages. This ends up being the worst of both worlds IMO: you get a bulky chain of errors, and each new layer tells you nothing. A FooBarError::Io(io::Error) doesn't tell you anything that just a libfoobar function throwing an io::Error and casting it to the general Error wrapper wouldn't also have told you. It gives no context.

As an example of how to use Context<D> from failure to do the contextual form:

#[derive(Debug, Fail)]
#[fail(display = "{}", inner)]
pub struct Error {
    inner: Context<ErrorKind>,
}

#[derive(Debug, Fail)]
enum ErrorKind {
   #[fail(display = "invalid Cargo.toml")]
    InvalidCargoToml,
    #[fail(display = "corrupted registry index for registry {}", _0)]
    InvalidRegistryIndex(String),
    #[fail(display = "network error")]
    NetworkError,
}

You can now use the .context() method to add an ErrorKind to an underlying error like an io::Error, a parsing Error, or an error in git.

I think 1 and 2 are both broadly applicable depending on the use case, whereas 3 (what error-chain encourages today) is rarely a good idea (basically, only if you can't afford to do the allocation in Error). But when do we recommend 1 over 2, or vice versa? How do users know whether they should introduce a contextual construct?

does not compile

I copy/pasted the example code to lib.rs, added the dependencies and ran cargo build. This is what I got:

# cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling toml v0.4.5
   Compiling test_failure v0.1.0 (file:///home/garrett/notes/rust/test_failure)
error[E0407]: method `cause` is not a member of trait `failure::Fail`
  --> src/lib.rs:18:17
   |
18 | #[derive(Debug, Fail)]
   |                 ^^^^ not a member of trait `failure::Fail`

error[E0407]: method `backtrace` is not a member of trait `failure::Fail`
  --> src/lib.rs:18:17
   |
18 | #[derive(Debug, Fail)]
   |                 ^^^^ not a member of trait `failure::Fail`

error[E0412]: cannot find type `Backtrace` in module `failure`
  --> src/lib.rs:18:17
   |
18 | #[derive(Debug, Fail)]
   |                 ^^^^ not found in `failure`

warning: unused `#[macro_use]` import
 --> src/lib.rs:6:1
  |
6 | #[macro_use] extern crate serde_derive;
  | ^^^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

error[E0046]: not all trait items implemented, missing: `fail`
 --> src/lib.rs:18:20
  |
1 | | extern crate failure;
  | |____^ missing `fail` in implementation
...
18|   #[derive(Debug, Fail)]
  |  ____________________^
  |
  = note: `fail` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>`

error: aborting due to 4 previous errors

error: Could not compile `test_failure`.

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

# rustc --version                                                                                                                                                                    
rustc 1.21.0 (3b72af97e 2017-10-09)  

# cat Cargo.toml                                                                                                                                                                     
[package]                                                                                                                                                                                                         
authors = ["Garrett Berg <[email protected]>"]
name = "test_failure"
version = "0.1.0"

[dependencies]
failure = "0.0.1"
failure_derive = "0.0.1"
serde = "1.0.20"
serde_derive = "1.0.20"
toml = "0.4.5"

Best practice to wrap IO error with message?

Let's say I'm doing some IO in a function, where several things can all fail with a std::io::Error. I want to include some message with that error. What's the best way to do this with failure? It looks like a custom error enum is a good way to start, but I'm unsure how to include the underlying io::Error as the underlying cause.

#[derive(Debug, Fail)]
enum MyCustomError {
    #[fail(display = "failed to read username file")]
    UsernameError,

    #[fail(display = "failed to read password file")]
    PasswordError
}

fn run() -> Result<(), MyCustomError> {

  // somehow put this `io::Error` into a MyCustomError::UsernameError
  let u : File = File::open(&username_file)?;

  // somehow put this `io::Error` into a MyCustomError::PasswordError
  let p : File = File::open(&password_file)?;

  Ok(())
}

Localization

In the talk given on Nov. 2 2017 in SF, an audience member asked about "localization". I'm not sure what specific localization techniques were being referred to in the discussion (they were never specified), but I have been using the technique of putting all string resources (for a particular locale) into a (build-time-interchangeable) module.

I merely refer to a constant (const _ &'static str) by name wherever it is needed.

Unfortunately, referring to a const in a Failure display attribute does not appear to be supported:

OK:

#[derive(Debug, Fail)]
enum ErrorKind {
    #[fail(display = "argument validation error")]
...

Error (error: proc-macro derive panicked):

// in en_us constants module
pub const MSG_ERR_ARG_VALIDATION: &'static str = "argument validation error";

// in error module
#[derive(Debug, Fail)]
enum ErrorKind {
    #[fail(display = MSG_ERR_ARG_VALIDATION)]
...

While I understand that rich evaluations like method calls in attributes are not supported by Rust today, given how attributes are processed, can this be remedied for constants?

Support 'compound failures'

Yes, this is a copy of rust-lang-deprecated/error-chain#1. I'd really like to find a nice solution to this.

I've recently run into a situation where I have a function which tries multiple subroutines returning Result<_, impl Failure> in order to produce its own output. But if all subroutines fail, it should return a descriptive failure which contains the cause, which in this case is that multiple other failures were produced.

Representing this well would require some sort of Vec<impl Failure> in a failure. However, because cause() and backtrace() are biased towards the single parent case, which is the overwhelming default behavior of error chaining, this is not a simple thing to express.

Improving error matching consistency (at least for testing)

TL;DR

Can Failure introduce a consistent interface that implements PartialEq across all Fail types so errors can be compared for equality simply and literally? This will improve the ergonomics and productivity around unit testing, and possibly other scenarios as well.

Motivation

As a BDD developer, the first thing I do when developing new code is to write a unit_test. Rust's lack of consistent API around errors makes writing unit tests less ergonomic than perhaps they should be. Here's a simple example of what I would like to write (which works, but only for some error types(!)):

#[test]
fn passing_bad_param_results_in_not_found_error() {
    assert_eq!(some_func(bad_param).err().kind(), std::io::ErrorKind::NotFound);
}

I've run into what I consider to be major ergonomic issues with errors and unit testing. I haven't found a consistent way to work with errors that doesn't involve a lot of pattern-matching boilerplate. Here are some details adapted from a posting I made about a month ago:

No consistent basis with which to work with Errors

(inconsistencies or issues in bold)

using the Standard Library

  • e.g. std::io::Error:

    • is a struct encapsulating an enum
    • does not implement PartialEq
    • does implement a kind() method
    • does implements ErrorKind variants which implement PartialEq
  • e.g. std::fmt::Error:

    • is a (marker) struct
    • does implement PartialEq (among other traits)
    • does not implement a kind() method
    • does not define ErrorKind variants
  • e.g. std::io::CharsError

    • is an enum
    • does not implement PartialEq
    • does not implement a kind() method
    • does not define ErrorKind variants
  • std::error::Error

    • is a trait
    • does not mandate/require errors implement PartialEq
    • does not mandate/require a kind() method
    • does hot mandate/require ErrorKind variants

using error-chain

  • error-chain-created errors
    • do not implement PartialEq
    • do implement a kind() method
    • do implement ErrorKind variants which do not implement PartialEq

using pattern matching

  • pattern-matching (as opposed to via PartialEq) is often cited as the recommended way to distinguish Errors, given these numerous inconsistencies). There are crates (such as matches) for reducing boilerplate when pattern-matching
  • unfortunately, pattern matching Errors adds boilerplate, obfuscates code and is an incomplete solution.

For example:

fn string_validator_ref_handles_empty_input() {
  // GIVEN an empty string
  let input = String::new();
  let expected_result = ErrorKind::ValueNone;

  // WHEN some operation which generates an error occurs
  let result = (&input).validate_ref::<NonEmptyStringValidator>().err().unwrap()
/* What I would *like* to be able to write */

// THEN ensure the expected error was returned
  assert_eq!(*result, expected_result);
}
/* The reality */

// THEN ensure the expected error was returned
  assert!(matches!(*result, expected_result); //Error: pattern unreachable (match doesn't work this way; unintuitive at this level)
  assert!(matches!(*result, val if val == expected_result)); //not ergonomic; Error without PartialEq (e.g. error-chain): binary operation `==` cannot be applied to type `error::ErrorKind`
  assert!(matches!(*result, ErrorKind::ValueNone); //works, but without variable support, important scenarios become unrepresentable:
      // suppose two methods must yield same result.  To ensure consistency through refactorings, a test is developed:
      // assert_eq!(matches(FuncA(badInput), FuncB(badInput)) //not possible, as per above; only constant variants w/PartialEq implemented can be used ergonomically

When considering Error equality, I believe it is out of scope for to consider the semantics of errors (e.g. "Are two instances of an error's Other variant equal?" Assuming the two instances of Other are value-equal then, yes. The fact that Other could be used by a library or application as a 'catch-all' to represent a wide variety of different error conditions in the real world is out of scope for this proposal. It is expected that the user of that error type will understand when a comparison of Other is and is not a meaningful operation).

Given that the Error type is freqently implemented using different types (marker structs, encapsulating structs, enums, and others) the only dependencies we should take are on its API. To that end, I would love to see a consistent .kind() method (or something else which serves the purpose of providing the error variant) which exists and is properly defined for every Failure.

Clarify rationale

The crate's readme says the crate intends to replace error management in Rust, taking lessons from problems with quick-error and error-chain.

It is intended to replace error management based on std::error::Error with a new system based on lessons learned over the past several years, including those learned from experience with quick-error and error-chain.

Could someone clarify what these problems are with the aforementioned crates, and how this crate aims to solve them?

Empty enums don't derive Display

extern crate failure;
#[macro_use]
extern crate failure_derive;

#[derive(Fail, Debug)]
//       ^^^^ error[E0277]: the trait bound `Never: std::fmt::Display` is not satisfied
pub enum Never { }

  --> lib.rs:17:10
   |
17 | #[derive(Fail, Debug)]
   |          ^^^^ `Never` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
   |
   = help: the trait `std::fmt::Display` is not implemented for `Never`

(workaround in the interim)

extern crate failure;
#[macro_use]
extern crate failure_derive;

#[derive(Fail, Debug)]
pub enum Never {
    #[fail(display="wanna")]
    Let(You),
}

#[derive(Debug)] pub struct You(Go);
#[derive(Debug)] pub enum Go { }

Incomplete sentence in docs

In the page, "The Fail trait", the following sentence seems to be unfinished:

It has both a backtrace and a cause method, allowing users to get

Use Any trait instead of magical __private_get_type_id__ method for downcasting

The Fail trait currently has a hidden __private_get_type_id__() method which gets the TypeId of a type to support downcasting. Would it be better to explicitly add Any as a trait bound to Fail instead of the hidden method?

pub trait Fail: Display + Debug + Send + Sync + 'static {

    ...

    #[doc(hidden)]
    fn __private_get_type_id__(&self) -> TypeId {
        TypeId::of::<Self>()
}

If all Fails implement Any that also means we'll be able to avoid the pointer casting being done in Fail::downcast_ref() and friends and leave the responsibility of ensuring correctness to the standard library.

I'm sure you've considered this in the past seeing as the implementation of Fail::downcast_ref() is almost identical to the one for Any, so what was the justification for adding a #[doc(hidden)] function for allowing downcasting?

Provide a standard way of printing/formatting an Error / Fail

It would be nice if there was some standard way of printing/formatting an Error or something implementing Fail.

Something that takes a &Write, maybe with a convenience helper that takes stderr/stdout, and prints the error itself, its description, its backtrace, and does the same for all the causes of it. Maybe parts of this should be configurable (don't print backtraces, don't recurse into causes).

Macro for matching multiple errors?

Is this a likely pattern that we'd want to automate, or is it discouraged?

if let Err(e) = try_to_do_the_thing() {
    match_err! { (e)
        me: MyError => println!("MyError: {}", me),
        io: io::Error => println!("IO: {}", io),
        unk => println!("unknown: {}", unk)
    }
}

I've verified that it's possible to implement this, if we want it :)

Ideal representation of Error

In theory, error could:

  1. Be 1 pointer in size.
  2. Never allocate if backtraces are turned off & the failure is a ZST.

To accomplish this goal, error would look like this:

struct Error {
     inner: usize,
}

This usize would be a pointer to a heap allocation or a vtable. Which kind of pointer it is would be stored using the extra bits of space that alignment guarantees give us on each platform.

The heap allocation would have this layout:

struct Inner {
    vtable: &'static (),
    backtrace: Backtrace,
    data: Fail, // A DST
}

I'm not sure its even possible to write this with unsafe code on stable Rust.

cc @cramertj @dtolnay #9 #20.

Context::with_err is private

Perhaps I'm doing this wrong, but should Context::with_err be private?

In attempting to follow the best-practices advice from issue #6, I am attempting to implement my custom error type as a struct, with enum ErrorKind variants.

I suppose I'm not following best practices advice by loading a variant with a cause (advice on how best to do this is welcome), but the way I attempted to do this requires access to the Context::with_err static constructor method, which is private. Is this intentional?

Here is my code:

use failure::Context;
use assayer::Error as AssayerError;

#[derive(Debug, Fail)]
#[fail(display = "{}", inner)]
pub struct Error {
    inner: Context<ErrorKind>,
}

#[derive(Debug, Fail)]
enum ErrorKind {
    #[fail(display = "argument validation error")]
    #[cause] ArgValidation(AssayerError),
}

impl From<AssayerError> for Error {
    fn from(err: AssayerError) -> Error {
        Error { inner: Context::with_err(ErrorKind::ArgValidation(err), err), }
    }
}

The above yields error[E0624]: method `with_err` is private.
If I am using Failure wrong, my apologies. Suggestions/advice welcome!

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.