If `Into <String>` is not used for `& String`, why do these implementations contradict each other?

I asked the appropriate question about why From<&String> for String does not exist. Now I want to create my own characteristic as follows:

 #[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); } 

Now the compiler claims that the two implementations of MyIntoString contradict each other. This is even strange for me, as we saw in another question that From<&String> did not implement for String , and therefore it did not find an implementation of Into<String> for &String . So why is this now in conflict?

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

Error message

According to one answer to this question, it seems that the error message did not lead me to the correct path.

So let me post an error message so that it may change 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` 

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

+5
source share
2 answers

The error is caused by orphan rules (see the "Second book" section of chapter 10.2 at the end Implementation of a characteristic by type ).

This prevents code breaking with minor changes (in accordance with RFC # 1105 ) in the boxes you use. If the authors of the standard library decided to implement Into<String> for &String , then your program will contain an inconsistent definition for my_into and it will break. Adding a trait implementation should be a minor change and should not violate your program.

This post provides rationale for the rule.

The book suggests using the newtype pattern to get around this problem.

 #[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

+3
source

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 cannot be specialized, because the compiler cannot decide which version is larger than specialized

Edit

Why is the code from the previous question not compiling? Because when you pass &s to new in

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

the compiler treats &s as &String , and from the code in std it sees:

 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> not implemented for String , it shows a compilation error. Therefore, you must explicitly say that &s is the type from which String can be built, i.e. &str

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

and now the compiler can see this

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

Now the compiler claims that the two implementations of MyIntoString contradict each other. This is even strange for me, as we already see in another> question that From <& String> did not implement for String, and therefore it did not find an implementation of Into for & String

The error occurs only because the compiler cannot decide which implementation is more specialized , even if you use specialization.

0
source

Source: https://habr.com/ru/post/1269860/


All Articles