Issues with trait specialization and failure

Code:

extern crate failure;

use failure::{Error, Fail};
use std::fmt;

#[derive(Debug)]
struct ContextError {
    msg: String,
    inner: Error,
}

trait ResultContextExt<T> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError>;
}

impl ContextError {
    fn new(msg: String, inner: Error) -> ContextError {
        ContextError { msg, inner }
    }
}

impl Fail for ContextError {
    fn cause(&self) -> Option<&Fail> {
        Some(self.inner.as_fail())
    }
}

impl fmt::Display for ContextError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.msg)
    }
}

impl<T, E: Fail> ResultContextExt<T> for Result<T, E> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError> {
        self.map_err(move |err| ContextError::new(msg.into(), Error::from(err)))
    }
}

impl<T> ResultContextExt<T> for Result<T, Error> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError> {
        self.map_err(move |err| ContextError::new(msg.into(), err))
    }
}

fn main() {
}

Error:

error[E0119]: conflicting implementations of trait `ResultContextExt<_>` for type `std::result::Result<_, failure::Error>`:
  --> src/main.rs:40:1
   |
34 | impl<T, E: Fail> ResultContextExt<T> for Result<T, E> {
   | ----------------------------------------------------- first implementation here
...
40 | impl<T> ResultContextExt<T> for Result<T, Error> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::result::Result<_, failure::Error>`
   |
   = note: upstream crates may add new impl of trait `std::error::Error` for type `failure::Error` in future versions

I am completely puzzled by this error. The first impl trait should only target types which implement Fail, and Error does not implement Fail, so how can the second impl be a conflicting implementation? Am I missing here something?

My current workaround for this is to duplicate the trait with a different name and use that for the second impl trait. Well, this works quite good, but this seems rather hacky and is probably not idiomatic rust code.

So what’s the best solution here?

Any why is this working:

trait ResultExt {}
trait Fail: 'static  {}

struct Error {
    err: Box<Fail>
}

struct ContextError {
    msg: String,
    err: Error
}

trait ResultContextExt<T> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError>;
}


impl Fail for ContextError {
    
}

impl<T, E: Fail> ResultContextExt<T> for Result<T, E> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError> {
        self.map_err(move |err| ContextError{msg:msg.into(), err: Error { err: Box::new(err)}})
    }
}

impl<T> ResultContextExt<T> for Result<T, Error> {
    fn wrap_err<S: Into<String>>(self, msg: S) -> Result<T, ContextError> {
        self.map_err(move |err| ContextError{msg:msg.into(), err})
    }
}

impl<T> ResultExt for Result<T, Error> {
    
}

fn main() {
    
}

playground: https://play.rust-lang.org/?gist=e2204e516fb42691e20154f1e249553e&version=stable&mode=debug&edition=2015

Sorry, I was mistaken. I have no idea why this works then :smiley:

This is allowed because you own the Fail trait (ie it’s in your crate and you have control over it).

This “negative” reasoning is mentioned in https://github.com/rust-lang/rfcs/blob/master/text/1023-rebalancing-coherence.md#type-locality-and-negative-reasoning, specifically the following paragraph:

1 Like

Thanks for that! But I am still not sure how to proceed here. The drawback of that RFC is quite cumbersome. Should I stick with the two-traits solution? Or what alternatives are there?