Multiple From implementation overlapping

I have the following (simplified) code:

pub trait Special {}

pub struct Id(String);

impl From<usize> for Id {
    fn from(id: usize) -> Self {
        Id(id.to_string())
    }
}

impl From<&str> for Id {
    fn from(sid: &str) -> Self {
        Id(sid.to_string())
    }
}

impl From<String> for Id {
    fn from(sid: String) -> Self {
        Id(sid)
    }
}

pub enum SpecialOrId<S> {
    Special(S),
    Id(Id)
}

impl<S: Special, ID: Into<Id>> From<ID> for SpecialOrId<S> {
    fn from(id: ID) -> Self {
        Self::Id(id.into())
    }
}

impl<S: Special> From<S> for SpecialOrId<S> {
    fn from(s: S) -> Self {
        Self::Special(s)
    }
}

Which produce the error:

error[E0119]: conflicting implementations of trait `std::convert::From<_>` for type `SpecialOrId<_>`
  --> src/lib.rs:34:1
   |
28 | impl<S: Special, ID: Into<Id>> From<ID> for SpecialOrId<S> {
   | ---------------------------------------------------------- first implementation here
...
34 | impl<S: Special> From<S> for SpecialOrId<S> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `SpecialOrId<_>`

From my understanding this is because someone might implement the Special trait for usize, &str or String which would lead to a conflicting implementation, is it right?

How to solve this? From my research on the web I have seen that implementing a Marker trait for only the allowed types would help this: generics - Conflicting implementations of trait in Rust - Stack Overflow. But I'm not sure to understand how to do that...

Thanks for the help.

Correct.

You could just drop the extra level of generics and just implement From<Id> and From<S> where S: Special. The compiler knows you've defined Id in the same crate as the Special trait and haven't implemented Special for it, so therefore there is no overlap.

(playground)

Alternatively, you could create specific constructors (e.g. SpecialOrId::from_id() and SpecialOrId::from_special()) instead of using the more general From trait.

2 Likes

I understand, but the Idea of the SpecialOrId struct is to be an helper struct for easy argument passing to other functions.

As such I would not like to use other functions else implicit conversion will not work.
That's also why I prefer to use the From, Into pattern.

If you've only got two From impls for Id, why not just implement From<&str> and From<String> for SpecialOrId directly?

(playground)

2 Likes

Well I only got two or three for now... But in the future this may grow...