If `Into` is not implemented for `&String`

2019-06-21 07:17发布

问题:

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.

回答1:

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



回答2:

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.



标签: rust traits