Code Monkey home page Code Monkey logo

snafu's Introduction

SNAFU

Situation Normal: All Fouled Up

crates.io Documentation Build Status

SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context.

use snafu::prelude::*;
use std::{fs, io, path::PathBuf};

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(display("Unable to read configuration from {}", path.display()))]
    ReadConfiguration { source: io::Error, path: PathBuf },

    #[snafu(display("Unable to write result to {}", path.display()))]
    WriteResult { source: io::Error, path: PathBuf },
}

type Result<T, E = Error> = std::result::Result<T, E>;

fn process_data() -> Result<()> {
    let path = "config.toml";
    let configuration = fs::read_to_string(path).context(ReadConfigurationSnafu { path })?;
    let path = unpack_config(&configuration);
    fs::write(&path, b"My complex calculation").context(WriteResultSnafu { path })?;
    Ok(())
}

fn unpack_config(data: &str) -> &str {
    "/some/path/that/does/not/exist"
}

Please see the documentation and the user's guide for a full description.

snafu's People

Contributors

ahdinosaur avatar akiradeveloper avatar arekpiekarz avatar asomers avatar atul9 avatar ccfb3ee765a58cae avatar chanced avatar cryze avatar cyborus04 avatar dependabot-preview[bot] avatar derickeddington avatar dimfeld avatar enet4 avatar erichdongubler avatar fan-tom avatar hellow554 avatar henning-k avatar ibraheemdev avatar isaccbarker avatar jasperdesutter avatar lucab avatar oliveruv avatar robzz avatar samwilsn avatar seritools avatar shepmaster avatar silwol avatar stargateur avatar taiki-e avatar tjkirch 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

snafu's Issues

Better handle functions that return Results that aren't errors

When a function uses a non-std::error::Error implementing type, there's no obvious way of using the context selector infrastructure:

fn example() -> Result<i32, bool> { /* ... */ }

One solution:

Add a method parallel to fail to construct unwrapped error instances:

example().map_err(|e| Selector { e }.into_error())?

This function has come up once or twice before. Could this actually be From<Selector> for Error:

example().map_err(|e| Selector { e })?

Feedback from a new user.

I have decided to give snafu a shot. I'll provide unsolicited feedback in this thread, and keep adding stuff as I bump into them. Feel free to close & mute or even tell me to go away.

I landed at https://docs.rs/snafu/0.4.1/snafu/index.html , looked at the example. Seemed straightforward. I went into coding head-on.

First bump was:

error[E0271]: type mismatch resolving `<Redis as snafu::IntoError<Error>>::Source == redis::types::RedisError`
  --> src/main.rs:22:58
   |
22 |     let redis = redis::Client::open("redis://127.0.0.1").context( Redis );
   |                                                          ^^^^^^^ expected struct `snafu::NoneError`, found struct `redis::types::RedisError`
   |
   = note: expected type `snafu::NoneError`
              found type `redis::types::RedisError`

"What does this thing want?" open does not return Option. I looked back and forth, scratched my head a lot, and finally figure out that source is special, and I have to define it as a field in my enum.

So far it was:

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

type Result<T, E = Error> = std::result::Result<T, E>;

and nowhere was it told (or I haven't noticed) that source is special. Now I get it, and it makes sense, but it's a bump and googling didn't help when I tried.

Add an optional derive that implements Debug by delegating to Display

What I don't particularly like is that both Debug and Display are used for errors in Rust. Most libraries and functions only focus on either the Debug or Display of the error. .unwrap() and fn main() -> Result<(), Error> tend to print the Debug implementation of the error. However SNAFU only focuses on providing Display implementations. So you end up deriving Debug for your errors which results in a struct / enum representation getting printed instead of the error messages on .unwrap() or when main fails. This is super unfortunate. I think it makes sense to have a derive for Debug that simply forwards to the carefully crafted error messages that we already have via Display.

Add an extension trait for Option as well

Option::ok_or / Option::ok_or_else are common ways of generating errors; we should support that.

Something like

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

let x: Option<i32> = None;
x.context(Missing)?;

I think this should only apply to types without a source. We may need to create our own NoneError to provide to the Context struct and will need to generate appropriate From implementations.

Add a pessimistic Backtrace type

It's hard to tell if a foreign error type has a backtrace or not. If it does, we don't want to create our own, otherwise we do.

It could be interesting to try and have some type that checks if the underlying error has a backtrace. If so, it does nothing. Otherwise, it generates one at that point.

Add a glossary

Terms:

  • attribute
  • context selector
  • user field
  • opaque error

Review and address feedback

From @Canop:

  • what's exactly context ? A kind of unwrap decorator ? How exactly is it added ?
  • what's the added cost (unwrapping complexity, structure creation before errors) ?
  • what are the limits and problems of snafu ?
  • how does it compare to other approachs (for example custom_errors) ?
  • does the Error enum automatically implement Display ?
  • are there facilities to display the stacktrace on error ?

Allow delegating the backtrace

If we have an underlying error that also has a backtrace, we could just forward the backtrace call to it.

Perhaps something to be handled by the hypothetical [snafu::source] or [snafu::backtrace] attributes.

Discuss attribute naming consistency

I really wanted snafu::FOO to be the attribute naming scheme, allowing for namespacing that seems a bit more beautiful compared to snafu_FOO. In order to support current and older versions of Rust, a compromise had to be made.

It now turns out that snafu::FOO might not even be deliberately supported. Once we add a few more attributes, we should re-evaluate and ensure they are consistent and decide a plan for the next breaking changes release.

Investigate alternatives to using `Borrow`

The root problem boils down to the implementation of Error::source. This code works, but the match arms are heterogeneous and cannot be automatically determined / generated via a procedural macro:

use std::{error, fmt, io};

#[derive(Debug)]
enum Error {
    TraitObjectSendSync(Box<dyn error::Error + Send + Sync + 'static>),
    TraitObjectSend(Box<dyn error::Error + Send + 'static>),
    TraitObjectSync(Box<dyn error::Error + Sync + 'static>),
    TraitObject(Box<dyn error::Error + 'static>),
    Boxed(Box<io::Error>),
    Unboxed(io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        Ok(())
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        use Error::*;

        Some(match *self {
            TraitObjectSendSync(ref e) => &**e,
            TraitObjectSend(ref e) => &**e,
            TraitObjectSync(ref e) => &**e,
            TraitObject(ref e) => &**e,
            Boxed(ref e) => e,
            Unboxed(ref e) => e,
        })
    }
}

The "safest" solution is to require that users annotate if the source is a trait object:

#[derive(Snafu)]
enum Error {
    Boom { 
        #[snafu(source(trait_object))]
        source: Box<dyn std::error::Error>,
    },
}

This sucks from an ergonomics perspective.

We could do some heuristics of "does the type contain Box" or "does the type contain dyn", but any heuristic can fail and we'd need a way to override them. Maybe it's Rc and not Box or the user doesn't use dyn, or they have a type alias for the trait object.

See also:

Add a features list to the guide

Maybe also to the README?

Things to include:

  • Easy Display
  • Context
  • Procedural macro
  • Backtraces
  • Forwards shims
  • Result & Option
  • Compatibility

Trouble getting basic example working

I've been trying to get snafu working in my project, and have been running into what must be beginners issues.

There are two error messages I'm hitting:

pub fn create_project(name: &str) -> LocalResult<()>
{
    with_io_error().context(error::CatchAllError{mesg:"Eeek an error!"})?;
    Ok(())
}}

is giving me

error[E0603]: struct `CatchAllError` is private

where CatchAllError is one of my snafu error types/contexts, but is defined in another file.

The same line is also giving me

error[E0277]: the trait bound `error::SeparateError: std::convert::From<snafu::Context<std::io::Error, error::CatchAllError<&str>>>` is not satisfied

I'm not sure how to get rid of either of these.

In the interest of a minimal reproducer, I've created the following repository, which you should be able to try to build and generate the issue.

https://github.com/mikeando/rust_snafu_test

I'm using:

$ rustc --version
rustc 1.33.0-nightly (9eac38634 2018-12-31)

so it may be specific to that version, and I can upgrade if you think it will fix things.

Add a FAQ section

  • the macro creates structs from the enum variants. Why is this? Why not just use enum variants?
  • when use structs, when use variants?

Cannot use Box<dyn Error + Send + Sync> as an error source

extern crate snafu;

use snafu::Snafu;

#[derive(Debug, Snafu)]
enum Error {
    Demo { source: Box<dyn std::error::Error + Send + Sync + 'static> }
}
error[E0277]: the trait bound `std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>: std::borrow::Borrow<dyn std::error::Error>` is not satisfied
 --> tests/repro.rs:5:17
  |
5 | #[derive(Debug, Snafu)]
  |                 ^^^^^ the trait `std::borrow::Borrow<dyn std::error::Error>` is not implemented for `std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>`
  |
  = help: the following implementations were found:
            <std::boxed::Box<Error> as std::borrow::Borrow<(dyn std::error::Error + 'static)>>
            <std::boxed::Box<T> as std::borrow::Borrow<T>>
  = note: required by `std::borrow::Borrow::borrow`

Originally reported in #74 by @ahdinosaur.

Where clauses from error types are not propagated to implementations

Given this code:

use crate::math::Op;
use std::fmt::Debug;

#[derive(Debug, PartialEq, Snafu)]
pub enum EvalError<T> where T: Debug {
    #[snafu(display("overflow occured while evaluating \"{:?} {} {:?}\"", left, op, right))]
    Overflow { left: T, op: Op, right: T },
    #[snafu(display("unknown error"))]
    Unknown
}

with op being defined as

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Op {
    Add,
    Sub,
    Mul,
    Div,
    Pow,
}

impl fmt::Display for Op {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Op::Add => "+",
                Op::Sub => "-",
                Op::Mul => "*",
                Op::Div => "/",
                Op::Pow => "^",
            }
        )
    }
}

rustc emits a missing std::fmt::Debug implementation, even though the type clearly has a generic constraint for exactly that.

error[E0277]: `T` doesn't implement `std::fmt::Debug`
  --> src/error.rs:23:28
   |
23 | #[derive(Debug, PartialEq, Snafu)]
   |                            ^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
   |
   = help: the trait `std::fmt::Debug` is not implemented for `T`
   = help: consider adding a `where T: std::fmt::Debug` bound
note: required by `error::EvalError`
  --> src/error.rs:24:1
   |
24 | pub enum EvalError<T> where T: Debug {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Removing snafu from the enum makes it compile as expected.

Rustc: rustc 1.35.0-nightly (3de010678 2019-04-11)
Cargo: cargo 1.35.0-nightly (6f3e9c367 2019-04-04)
Snafu: 0.2.2

Add an `ensure` macro

ensure!(some_boolean_expression, ContextSelector { args });

Expands to

if !some_boolean_expression {
    return ContextSelector { args }.fail();
}

Add short code example to README

Hi, this is a truly hit-and-run issue as I just came across this repo. However, I love seeing minimal code examples in READMEs for especially projects like these so that I can quickly compare it to what I'm otherwise used. This project's README is especially sparse so it'd make a good fit, I think. Just close this if you don't agree.

Investigate ways to avoid performance impacts of backtraces

Right now, if you create a backtrace-enhanced error but then handle it, you've paid for the cost to walk the stack and resolve the symbols for no reason. This may cause performance issues.

Two ideas I have:

  1. Use an environment variable to prevent backtraces from being generated

    Look for something like RUST_BACKTRACE=1 โ€” if it's not provided the backtrace becomes a no-op. Great for performance but sucks when you don't have it enabled when you need it.

    May want to make this a separate type so that people can opt-in to either behavior.

  2. Don't resolve the symbols until display time

    I believe the backtrace crate supports this. This would reduce the cost of generating the backtrace. This may be a "free" win and we should do it unconditionally.

Cannot use lifetime-parameterized errors as sources

Code like this wonโ€™t work:

#[derive(Debug, Snafu)]
enum Error<T> {
    Generic { source: GenericError<โ€˜a, T> },
}

We need a reference with the โ€™static bound to be able to call Error::source.

We could perhaps disable the ability to call Error::source by just returning None.

How can we better support no-std usecases?

Originally, SNAFU was going to support no-std, until I learned that the Error trait doesn't exist in libcore!

That being said, people have said that failure works will in no-std. Evidently, failure can just implement Display in this case.

We certainly could do the same, but I wonder if there's some other powers we could add. My wildest thought would be to establish a parallel Error trait that we could implement instead of (or in addition to?) std::error::Error.

Would this be something that ErrorCompat should do?

Allow visibility to be applied to context selectors

My opinion is that error types should be plentiful โ€” each module should have one (or more!) error types. Each error type is only ever constructed by that module (and maybe child modules). Each error type is very tailored to its domain and there are less cases of "but these 31 error variants don't actually occur here". This fits really well with SNAFU's default of making context selectors private by default.

However, the concept of a unified error type is present in the Rust ecosystem, and I have found myself wanting to create a child's error for purposes of testing. This would be best served via marking the context selectors with pub(crate). Once we open that can of worms, we can probably allow any visibility modifier.

How should we allow users to specify this? My first thoughts are something like this:

#[derive(Snafu)]
#[snafu::visibility(pub(crate)] // sets the default visibility (uses private if not present)
enum Error {
    Alpha,
    #[snafu::visibility(pub)] // sets the specific visibility (uses the default if not present)
    Beta,
}

Consider how to support tuple variants

People ask about tuple variants:

#[derive(Debug, Snafu)]
enum Error {
    Something(i32),
}

There are a few reasons I've avoided adding it this far.

Identifying source errors / backtraces

Right now, we tell what the underlying error is by using the name source (mirroring std::Error::source). #10 proposes extending this, so using the attribute from there, you'd be required to annotate the appropriate field:

#[derive(Debug, Snafu)]
enum Error {
    Something(#[snafu(source)] AnotherError, i32, #[snafu(backtrace)] Backtrace),
}

Context selectors would also be order-dependent

Since context selectors mirror the corresponding variant, they would become tuple structs. The automatically-handled fields would be removed, which means that the parameters of the selector are different:

#[derive(Debug, Snafu)]
enum Error {
    Something(Backtrace, String, AnotherError, i32),
}
fn example() {
    something().context(Something("name", 42))
}

Accessing the fields in Display has no standard syntax

Right now, we bind the fields in the variant to the given names, but tuples don't have valid names to use. We'd have to make up some syntax (like failure) or create some variable:

#[derive(Debug, Snafu)]
enum Error {
    // Bind them to variables with some prefix (here `_`)
    #[snafu(display("The {} is {}: {}", _1, _3, _2))]
    // Create another tuple with some fixed name (here `props`)
    #[snafu(display("The {} is {}: {}", props.1, props.3, props.2))]
    Something(Backtrace, String, AnotherError, i32),
}

None of the problems are insurmountable, but is this a pattern that we want to even encourage? What are the benefits of supporting tuple variants?

Error variants named `None` conflict with `Option::None`

If by chance someone chooses to name one of the variants None, the code expanded by derive(Snafu) will not compile.

use snafu::Snafu;

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(display("Nothing is wrong"))]
    None,
}
error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | #[derive(Debug, Snafu)]
  |                 ^^^^^ expected enum `std::option::Option`, found struct `None`
  |
  = note: expected type `std::option::Option<&dyn std::error::Error>`
             found type `None`

error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | #[derive(Debug, Snafu)]
  |                 ^^^^^ expected enum `std::option::Option`, found struct `None`
  |
  = note: expected type `std::option::Option<&(dyn std::error::Error + 'static)>`
             found type `None`

error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | #[derive(Debug, Snafu)]
  |                 ^^^^^ expected enum `std::option::Option`, found struct `None`
  |
  = note: expected type `std::option::Option<&snafu::backtrace_shim::Backtrace>`
             found type `None`

error: aborting due to 3 previous errors

Cannot create a recursive error type

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("{}", source))]
    Any { source: Box<Error> },
}
error[E0277]: the trait bound `std::boxed::Box<Error>: std::borrow::Borrow<dyn std::error::Error>` is not satisfied
 --> src/main.rs:3:17
  |
3 | #[derive(Debug, Snafu)]
  |                 ^^^^^ the trait `std::borrow::Borrow<dyn std::error::Error>` is not implemented for `std::boxed::Box<Error>`
  |
  = help: the following implementations were found:
            <std::boxed::Box<T> as std::borrow::Borrow<T>>
  = note: required by `std::borrow::Borrow::borrow`

Reported by @BurntSushi (#46)

Support additional ways of specifying the error source or backtrace

Right now, we only support an underlying error denoted by the field named source and a backtrace by the name backtrace. We should also support a field attribute (#[snafu(source)] / #[snafu(backtrace)]), with an optional boolean to disable the automatic (#[snafu(source(false))] / #[snafu(backtrace(false))])

Precedence wise, it should be:

  1. attribute
  2. source

From implementations for converting from source error types

I'm evaluating snafu as a replacement for quick-error and came across a couple of ergonomic issues that I thought would be worth mentioning. They are both related to implementing From such that the error variant is determined by the source error type.

Without context

First, let's consider the case where you want a variant that acts as a wrapper for a source error type with no additional context. For example, let's say you want an error type

pub enum MyError {
    Foo(FooError),
    Bar(BarError),
}

This represents an error that is created from either a source error of type FooError or a source error of type BarError with no additional context information. quick-error handles this nicer than snafu in two ways:

  1. It allows using a tuple variant (Foo(FooError)) instead of requiring a struct variant (Foo { source: FooError }).

  2. It makes it easy to create impl From<FooError> for MyError and impl From<BarError> for MyError. (quick-error makes this easy by having the user write from() to specify that they want a From implementation to be created.)

    This makes it possible to write

    // using quick-error
    fn my_fn() -> Result<(), MyError> {
        returns_result_with_foo_error()?;
        Ok(())
    }

    instead of having to explicitly specify the variant with

    // using snafu
    fn my_fn() -> Result<(), MyError> {
        returns_result_with_foo_error().context(Foo)?;
        Ok(())
    }

Item 1 isn't critical, but it would be nice to have. (It looks like discussion about this is in #61, so I won't talk about it more here.)

Item 2 is pretty important so that ? works without having to call .context(). I suppose you could argue that the user should always call .context() to explicitly specify the error variant, but when the variant can be determined directly from the source error type, IMO calling .context() is unnecessarily verbose.

With context

A related issue is when the error variant does have context information but the error variant can still be determined from the source error type. For example,

pub enum MyError {
    Foo {
        message: String,
        source: FooError,
    },
    Bar {
        message: String,
        source: BarError,
    },
}

Similar to the case without context, it would be nice to be able to use the ? operator without explicitly specifying the error variant. So, it would be nice to be able to write

// using quick-error
fn my_fn() -> Result<(), MyError> {
    returns_result_with_foo_error().context("message")?;
    Ok(())
}

instead of

// using snafu
fn my_fn() -> Result<(), MyError> {
    returns_result_with_foo_error().context(Foo { message: "message"})?;
    Ok(())
}

To do this, we need an easy way to create impl<C: Into<String>> From<Context<FooError, C>> for MyError (or just impl<'a> From<Context<FooError, &'a str>> for MyError).

quick-error makes this easy by allowing the user to write context(message: &'a str, source: FooError) -> (message.to_string(), source) to specify that they want a From<Context<&'a str, FooError>> implementation to be created [note: quick-error swaps the type parameters of Context].

Edit: By the way, I wanted to mention that your documentation is awesome!

Raw identifier not supported in the `display` attribute

Using snafu version 0.2.3 does not support raw identifiers (e.g. r#type).

This does not work currently:

use snafu::Snafu;

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(display("{}", r#type))]
    Oc { r#type: String }
}
cargo build
   Compiling proc-macro2 v0.4.28
   Compiling libc v0.2.51
   Compiling cc v1.0.35
   Compiling unicode-xid v0.1.0
   Compiling syn v0.15.32
   Compiling autocfg v0.1.2
   Compiling cfg-if v0.1.7
   Compiling rustc-demangle v0.1.14
   Compiling backtrace v0.3.15
   Compiling backtrace-sys v0.1.28
   Compiling quote v0.6.12
   Compiling snafu-derive v0.2.3
   Compiling snafu v0.2.3
   Compiling foo v0.1.0 (/tmp/tmp.mSGf3U1WvR/foo)
error: expected expression

error: aborting due to previous error

error: Could not compile `foo`.

Support AsErrorSource for error sources that don't implement std::error::Error

Since starting to use SNAFU I've repeatedly run into libraries whose error types implement Debug + Display but not std::error::Error.

Two options I can think of that might make this easier to deal with for users:

  • failure has a Compat struct which can just wrap the type, and implements Error if the underlying type implements Debug + Display. (I've been writing types like this as a workaround in my projects.)
  • Add implementations of AsErrorSource for dyn Debug + Display + 'static, etc.

Support a backtrace

People like backtraces.

My idea is to support a field called backtrace. If it's present, it will be automatically constructed when From::from or fail is called.

One trick will be to decide how best to have forwards-compatibility. Eventually, the standard library will have such a type and it will accessible via the Error trait, but it's not there today. We'd prefer to be able to be able to seamlessly upgrade.

My thought is to have a newtype for the backtrace crate that doesn't expose much in the way of an API. We can then add an inherent method on the Error enum with the proposed API for std::error::Error.

Things to check / consider:

  • What happens when std::error::Error gains the method; will that cause conflicts?
  • Do we need multiple ways to specify what the backtrace is? (see #10)

Use doc comments as default Display implementations

A lot of my errors look like this with snafu:

/// The Error type for splits files that couldn't be parsed by the FaceSplit
/// Parser.
#[derive(Debug, snafu::Snafu)]
pub enum ParseError {
    /// Expected the title, but didn't find it.
    #[snafu(display("Expected the title, but didn't find it."))]
    ExpectedTitle,
    /// Expected the goal, but didn't find it.
    #[snafu(display("Expected the goal, but didn't find it."))]
    ExpectedGoal,
    /// Expected the attempt count, but didn't find it.
    #[snafu(display("Expected the attempt count, but didn't find it."))]
    ExpectedAttemptCount,
    /// Expected the name of the segment, but didn't find it.
    #[snafu(display("Expected the name of the segment, but didn't find it."))]
    ExpectedSegmentName,
    /// Expected the split time, but didn't find it.
    #[snafu(display("Expected the split time, but didn't find it."))]
    ExpectedSplitTime,
    /// Expected the best segment time, but didn't find it.
    #[snafu(display("Expected the best segment time, but didn't find it."))]
    ExpectedBestSegmentTime,
}

As you can see all the display attributes are basically verbatim copied into the doc comments. It would be cool if Snafu used the doc comment as the display impl for a variant if there is no explicit snafu(display(...)) attribute.

Deny warnings in CI builds

This will also entail finding and fixing all existing warnings. #111 is addressing some of them, so this should happen after that.

Please make "backtraces" feature opt-in, not opt-out

Since #12 snafu supports capturing backtraces. While useful in several contexts, backtraces comes with additional costs both at runtime (#84) and at buildtime (it uses backtrace-sys under the hood, which brings in C source/compilation).

Backtraces are currently active by default and needs to be disabled (opt-out) by consumers via the default-features = false syntax.
In the context of an ecosystem of snafu-using libraries, this poses a problem when nesting a couple of levels of dependencies, as the top-level consumer is no more in charge of those features and can't directly opt-out.

Considering that cargo features are additive, a better approach would be to make backtraces disabled by default and let the final consumer enable them via a dedicated feature (opt-in).
That way any intermediate library could re-export the snafu/backtraces feature, and the top-level application/consumer would be the one (optionally) in charge of enabling backtraces across all crates.

Add the ability to make an opaque error type

Having a public enum for an error type means that the enum variants are also public and a part of the API. This isn't always desired.

We could add a way to delegate the important traits while hiding details:

#[derive(SnafuOpaque)]
// or `Snafu` + `#[snafu(opaque)]`?
// or just know that it's a tuple struct with one member?
struct Error(InternalError)

impl Error {
    // Deliberately chosen methods here
    fn is_caused_by_network(&self) -> bool { /* ... */ }
}

#[derive(Snafu)]
enum InternalError {
    Alpha,
    Beta,
}

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.