6

Given the following code:

extern crate dotenv; // v0.11.0
#[macro_use]
extern crate failure; // v0.1.1

#[derive(Debug, Fail)]
#[fail(display = "oh no")]
pub struct Error(dotenv::Error);

Due to the fact that error-chain (v0.11, the latest) does not guarantee that its wrapped error is Sync (open PR to fix the issue) I get an error saying that Sync is not implemented for dotenv::Error:

error[E0277]: the trait bound `std::error::Error + std::marker::Send + 'static: std::marker::Sync` is not satisfied
 --> src/main.rs:5:17
  |
5 | #[derive(Debug, Fail)]
  |                 ^^^^ `std::error::Error + std::marker::Send + 'static` cannot be shared between threads safely
  |
  = help: the trait `std::marker::Sync` is not implemented for `std::error::Error + std::marker::Send + 'static`
  = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<std::error::Error + std::marker::Send + 'static>`
  = note: required because it appears within the type `std::boxed::Box<std::error::Error + std::marker::Send + 'static>`
  = note: required because it appears within the type `std::option::Option<std::boxed::Box<std::error::Error + std::marker::Send + 'static>>`
  = note: required because it appears within the type `error_chain::State`
  = note: required because it appears within the type `dotenv::Error`
  = note: required because it appears within the type `Error`

What's the minimal amount of boilerplate/work I can get away with to make these types play nicely together? The shortest thing I've been able to come up with is adding an ErrorWrapper newtype so that I can implement Error on an Arc<Mutex<ERROR_CHAIN_TYPE>>:

use std::fmt::{self, Debug, Display, Formatter};
use std::sync::{Arc, Mutex};

#[derive(Debug, Fail)]
#[fail(display = "oh no")]
pub struct Error(ErrorWrapper<dotenv::Error>);

#[derive(Debug)]
struct ErrorWrapper<T>(Arc<Mutex<T>>)
where
    T: Debug;

impl<T> Display for ErrorWrapper<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "oh no!")
    }
}

impl<T> ::std::error::Error for ErrorWrapper<T>
where
    T: Debug,
{
    fn description(&self) -> &str {
        "whoops"
    }
}

// ... plus a bit more for each `From<T>` that needs to be converted into
// `ErrorWrapper`

Is this the right way to go about it? Is there something that will have less code/runtime cost for converting into an Arc<Mutex<T>> for things that don't need it?

1 Answer 1

1

The only thing that I can suggest is to remove the Arc type.

If you have something that is not Sync then wrap into a Mutex type, so to integrate error-chain with failure just wrap dotenv::Error:

#[macro_use]
extern crate failure;
extern crate dotenv;
use std::sync::Mutex;

#[derive(Debug, Fail)]
#[fail(display = "oh no")]
pub struct Error(Mutex<dotenv::Error>);

fn main() {
    match dotenv::dotenv() {
        Err(e) => {
            let err = Error(Mutex::new(e));
            println!("{}", err)
        }
        _ => (),
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the feedback @E_net4!
That is a good point that I don't need the Arc since I'm not actually sharing the value, just sending it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.