Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If `Into<String>` is not implemented for `&String`, why are these implementations conflicting?

Tags:

rust

traits

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.

like image 491
Earth Engine Avatar asked Oct 17 '22 09:10

Earth Engine


1 Answers

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

like image 96
red75prime Avatar answered Jan 04 '23 05:01

red75prime