4

Suppose there is a trait, whose methods all only take a reference of self, such as

trait Trait {
    fn foo(&self) -> i32;
}

I'd like to have this trait implemented for both Option<T> and Option<&T> (as I can't always afford ownership), with a trivial implementation such as

impl<T: Trait> Trait for Option<T> {
    fn foo(&self) -> i32 {
        if let Some(inner) = self { return inner.foo(); }
        0
    }
}

impl<T: Trait> Trait for Option<&T> {
    fn foo(&self) -> i32 {
        if let Some(inner) = self { return inner.foo(); }
        0
    }
}

However, doing so produces the following error:

error[E0119]: conflicting implementations of trait `Trait` for type `std::option::Option<&_>`:
  --> option.rs:12:1
   |
5  | impl<T: Trait> Trait for Option<T> {
   | ---------------------------------- first implementation here
...
12 | impl<T: Trait> Trait for Option<&T> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::option::Option<&_>`
   |
   = note: downstream crates may implement trait `Trait` for type `&_`

Moreover, the implementations are literally the same. Is it possible to do this in a more compact way?

cripplejayb
  • 73
  • 1
  • 4
  • Does this answer your question? [Implementing a trait for reference and non reference types causes conflicting implementations](https://stackoverflow.com/questions/48432842/implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im) – sshashank124 Jan 01 '20 at 17:00
  • It does not. I don't want to have one implementation for all reference types and another implementation for all non-reference types. I want to have the trait implemented for `Option` where `T` is any type method `.foo()` could be applied on, i.e. where if `bar` is an instance of `T`, calling `bar.foo()` is valid. – cripplejayb Jan 01 '20 at 17:08
  • As a workaround, you could implement your trait only for `Option<&T>` and document the fact that to use it with `Option` user must first call `as_ref`. This is not the solution, strictly speaking, so I'm not posting it as an answer, but it might be useful. – Cerberus Jan 01 '20 at 17:33
  • 1
    I have considered that, but then I can't use `derive` macro on structs containing an `Option` field to auto-derive the trait. – cripplejayb Jan 01 '20 at 17:36

1 Answers1

8

This does not compile because I, as a user of your trait could do something like this:

struct Yo;

impl Trait for Yo {
    fn foo(&self) -> i32 { 0 }
}
impl Trait for &Yo {
    fn foo(&self) -> i32 { 1 }
}

fn main() {
    let a = Yo;
    let b: Option<&Yo> = Some(&a);
    b.foo(); // ambiguous call!
}

And there are two conflicting implementations of your trait for Option<&Yo>! Unfortunately trait specialization is still unstable, so that is probably not an option.

In your particular case you may solve with this generic impl, if you are willing:

impl<T: Trait> Trait for &T {
    fn foo(&self) -> i32 {
        (*self).foo()
    }
}

This, combined with your generic impl for Option<T>, will give an unambiguous implementation for Option<&T>.

rodrigo
  • 94,151
  • 12
  • 143
  • 190