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.
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
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.