5

I asked a relevant question about why there is no implementation of From<&String> for String. I now want to create my own trait as the following:

#[derive(Debug)]
struct MyStruct(String);

impl MyStruct {
    fn new<T>(t: T) -> MyStruct
    where
        T: MyIntoString,
    {
        MyStruct(t.my_into())
    }
}

trait MyIntoString {
    fn my_into(self) -> String;
}

impl<'a> MyIntoString for &'a String {
    fn my_into(self) -> String {
        self.clone()
    }
}

impl<I> MyIntoString for I
where
    I: Into<String>,
{
    fn my_into(self) -> String {
        self.into()
    }
}

fn main() {
    let s: String = "Hello world!".into();
    let st: MyStruct = MyStruct::new(&s);
    println!("{:?}", st);
}

The compiler now claims that the two implementations of MyIntoString are conflicting. This is even weirder to me as we already see in the other question that From<&String> didn't implement for String and so it didn't find an implementation of Into<String> for &String. So how come this is conflicting now?

Furthermore, even when I turned on #![feature(specialization)], the same conflict was detected.

The error message

According to one answer of this question, it looks like the error message didn't guide me to the right track.

So let me post the error message to blame, as it may changed in the future.

error[E0119]: conflicting implementations of trait `MyIntoString` for type `&std::string::String`:
  --> src/main.rs:23:1
   |
17 | / impl<'a> MyIntoString for &'a String {
18 | |     fn my_into(self) -> String {
19 | |         self.clone()
20 | |     }
21 | | }
   | |_- first implementation here
22 |   
23 | / impl<I> MyIntoString for I
24 | | where
25 | |     I: Into<String>,
26 | | {
...  |
29 | |     }
30 | | }
   | |_^ conflicting implementation for `&std::string::String`

To me, this is a claim by the compiler that there is a REAL conflict, not a potential one.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Earth Engine
  • 10,048
  • 5
  • 48
  • 78
  • Related: https://stackoverflow.com/questions/45033704/why-do-i-get-conflicting-implementations-of-trait-for-f32-which-does-not-imple – Boiethios Jul 17 '17 at 08:44

2 Answers2

3

The error is caused by the orphan rules (see The Book second ed. chapter 10.2 at the end of Implementing a trait on a type).

These prevents your code from breaking when there are minor changes (as per RFC#1105) in crates you use. If the authors of the standard library decided to implement Into<String> for &String, then your program would contain a conflicting definition for my_into and would break. The addition of a trait implementation should be a minor change and shouldn't break your program.

This post provides justification for the rule.

The Book suggests using the newtype pattern to work around this issue.

#[derive(Debug)]
struct MyStruct(String);

impl MyStruct {
    fn new<T>(t: T) -> MyStruct
    where
        T: Into<String>,
    {
        MyStruct(t.into())
    }
}

struct Wrapper<'a>(&'a String);

impl<'a> From<Wrapper<'a>> for String  {
    fn from(t: Wrapper<'a>) -> String {
        t.0.clone()
    }
}

fn main() {
    let s: String = "Hello world!".into();
    let st: MyStruct = MyStruct::new(Wrapper(&s));
    println!("{:?}", st);
}

Playground link

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
red75prime
  • 3,733
  • 1
  • 16
  • 22
  • 1
    This code does the same thing as mine but in a completely different way. This code implements `Into` on `Wrapper<'a> == &'a String`, but my code implements `MyIntoString` on `&'a String`. Both should be passed the orphan rule. – Earth Engine Jul 16 '17 at 12:51
  • 2
    No. Blanket implementation of `MyIntoString` (that is `impl> MyIntoString for I`) combined with `impl MyIntoString for &String` creates potential for code breakage caused by minor changes in standard library. Newtype variant will not break. – red75prime Jul 16 '17 at 12:59
  • 2
    I don't see how the orphan rule is relevant, since OP is implementing his own trait `MyIntoString`. – interjay Jul 16 '17 at 13:05
  • The orphan rules are made to allow for *negative reasoning* ("trait Y is not implemented for type X"), which is necessary from time to time. But OP's original problem is that the orphan rules are not strong enough in this case. We cannot apply negative reasoning to assume that `From<&String>` is not implemented for `String` (because such an impl can be added by a non-breaking change). But I guess you could explain this without mentioning "orphan rules" at all :/ – Lukas Kalbertodt Jul 16 '17 at 13:17
  • @LukasKalbertodt, "orphan rule" is a relevant search term if one wants to explore the topic. – red75prime Jul 16 '17 at 13:36
  • I think now I understand what the real problem is. The "conflict" is only potential, but the error message didn't even include this fact, and so I get confused when the "confilict" was proved that does not exist. So, it is the error message needed to blame. – Earth Engine Jul 16 '17 at 22:47
  • And unfortunately, the solution or workaround in this answer is not better than calling `clone()` on the usage side. So it is not even a proper "workaround". But that is what we can get before specification stablised. – Earth Engine Jul 17 '17 at 00:48
  • I adjusted this answer as the document of `Into` said we should not implement `Into` directly. Implement `From` instead. – Earth Engine Jul 17 '17 at 04:32
0

This code works with specialization

#![feature(specialization)]

#[derive(Debug)]
struct MyStruct(String);

impl MyStruct {
    fn new<T>(t: T) -> MyStruct
    where
        T: MyIntoString,
    {
        MyStruct(t.my_into())
    }
}

trait MyIntoString {
    fn my_into(self) -> String;
}

impl<'a> MyIntoString for &'a String {
    fn my_into(self) -> String {
        self.clone()
    }
}

default impl<I> MyIntoString for I 
{
    default fn my_into(self) -> String {
        String::from("FOO")
    }
}

fn main() {
    let s: String = "Hello world!".into();
    let st: MyStruct = MyStruct::new(&s);
    println!("{:?}", st);
}

So, AFAIU, your version can't be specialized since compiler can't decide which version is more specialized

edit

Why code from previous question doesn't compile? Because when you pass &s to new in

    let st: MyStruct = MyStruct::new(&s);

compiler consider &s as &String, and from code in std it see:

impl<T, U> Into<U> for T where U: From<T>

impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U {
        U::from(self)
    }
}

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

and since From<&String> is not implemented for String it show compile error. So you must explicitly say that &s is type from which String can be constructed, that is &str

    let st: MyStruct = MyStruct::new(&s as &str);

and now compiler can see this

impl<'a> From<&'a str> for String

The compiler now claim that the two implementation of MyIntoString are > conflicting. This is even weirder to me as we already see in the other > question that From<&String> didn't implement for String and so it didn't find an implementation of Into for &String

The error happen just because compiler cannot decide which implementation is more specialized even if you use specialization.

Oleh Devua
  • 364
  • 6
  • 12
  • The main point of this question is not about specialization. It is about why `&'a String` is considered as implemented `Into` in this context but not in another. – Earth Engine Jul 16 '17 at 09:40
  • because of automatic dereferencing – Oleh Devua Jul 16 '17 at 09:46
  • Please explain in your answer, along with your workaround. – Earth Engine Jul 16 '17 at 09:49
  • `&String` can be considered as one which implement `Into` when you try call `into` on its instance because of `automatic dereferencing`. But, in my code `&String` considered as `MyIntoString` because of I implemented this trait for `&String`. In your code it doesn't work because misusing of `specialization`. – Oleh Devua Jul 16 '17 at 10:12
  • If you read my other question https://stackoverflow.com/questions/45118060/why-doesnt-string-implement-fromstring, you know I am actually not able to call `into` on a `&String`. So I didn't understand why it is automatic dereferenced here but not there. – Earth Engine Jul 16 '17 at 10:15
  • Code from your previous question doesn't compiles since compiler dereferenced `&s` in type you didn't expected. To fix that you need to write `let st: MyStruct = MyStruct::new(&s as &str);` – Oleh Devua Jul 16 '17 at 10:31
  • I didn't understand. Yes your "fix" works, but only when I introduce a new type `&str` rather than `&String`. But I was expected an implementation for `&String`, not `&str`. So, if no implementation found for `&String` why it implements `Into`? – Earth Engine Jul 16 '17 at 10:39
  • `So, if no implementation found for &String why it implements Into` -- it doesn't. It's not implemented in `std`. Why? I think because you can convert &String into String with dereferencing, that most natural way to do it. – Oleh Devua Jul 16 '17 at 11:20
  • Putting things together, it is weird isn't it: the compiler think you are able to call `into` on an `&String` so it don't let you implementing "conflicting" implementations. But as soon as you try to call `into` on `&String` the compiler said you cannot because you didn't implement for this. – Earth Engine Jul 16 '17 at 12:01