【问题标题】:If `Into<String>` is not implemented for `&String`, why are these implementations conflicting?如果 `&String` 没有实现 `Into<String>`,为什么这些实现会发生冲突?
【发布时间】:2017-07-17 20:29:04
【问题描述】:

我向relevant question 询问了为什么String 没有实现From&lt;&amp;String&gt;。我现在想创建自己的 trait,如下所示:

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

编译器现在声称MyIntoString 的两个实现存在冲突。这对我来说更奇怪,因为我们已经在另一个问题中看到From&lt;&amp;String&gt; 没有为String 实现,因此它没有为&amp;String 找到Into&lt;String&gt; 的实现。怎么现在这么矛盾了?

此外,即使我打开#![feature(specialization)],也检测到相同的冲突。

错误信息

根据此问题的一个答案,错误消息似乎没有将我引导到正确的轨道。

所以让我把错误信息贴出来,因为它将来可能会改变。

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`

对我来说,这是编译器声称存在真正的冲突,而不是潜在的冲突。

【问题讨论】:

标签: rust traits


【解决方案1】:

该错误是由孤儿规则引起的(参见本书第二版第 10.2 章 Implementing a trait on a type 末尾)。

这些可以防止您的代码在您使用的 crate 中发生微小更改时(根据 RFC#1105)中断。如果标准库的作者决定为&amp;String 实现Into&lt;String&gt;,那么您的程序将包含my_into 的冲突定义并且会中断。添加 trait 实现应该是一个小改动,并且不应该破坏你的程序。

This post 为规则提供了理由。

本书建议使用newtype pattern 来解决此问题。

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

【讨论】:

  • 这段代码和我的代码一样,但方式完全不同。此代码在Wrapper&lt;'a&gt; == &amp;'a String 上实现Into&lt;String&gt;,但我的代码在&amp;'a String 上实现MyIntoString。两者都应该通过孤儿规则。
  • 没有。 MyIntoString(即impl&lt;I: Into&lt;String&gt;&gt; MyIntoString for I)的一揽子实现与impl MyIntoString for &amp;String 相结合,可能会因标准库中的微小更改而导致代码损坏。新类型变体不会中断。
  • 我看不出孤儿规则是如何相关的,因为 OP 正在实现他自己的 trait MyIntoString
  • 孤儿规则允许否定推理(“特征 Y 未针对类型 X 实现”),这是不时需要的。但是OP的原始问题是孤儿规则在这种情况下不够强大。我们不能应用否定推理来假设 From&lt;&amp;String&gt; 没有为 String 实现(因为可以通过非破坏性更改添加这样的 impl)。但我想您可以完全不提及“孤儿规则”来解释这一点:/
  • @LukasKalbertodt,如果人们想探索该主题,“孤儿规则”是一个相关的搜索词。
【解决方案2】:

此代码适用于专业化

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

所以,AFAIU,你的版本不能专门化,因为编译器无法确定哪个版本更高specialized

编辑

为什么上一个问题的代码无法编译? 因为当你将&amp;s 传递给new

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

编译器将&amp;s 视为&amp;String,并从std 中的代码中看到:

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

因为From&lt;&amp;String&gt; 没有为String 实现,所以它显示编译错误。所以你必须明确地说&amp;s是可以构造String的类型,即&amp;str

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

现在编译器可以看到这个

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

编译器现在声称 MyIntoString 的两个实现是 > 冲突的。这对我来说更奇怪,因为我们已经在另一个 > 问题中看到 From 没有为 String 实现,所以它 没有找到 Into for &String 的实现

发生错误只是因为编译器无法确定哪个实现更specialized,即使您使用专门化。

【讨论】:

  • 这个问题的重点不是专业化。这就是为什么 &amp;'a String 在这种情况下被认为是实现 Into&lt;String&gt; 而不是在另一种情况下。
  • 因为自动取消引用
  • 请在您的回答中解释您的解决方法。
  • &amp;String 可以被认为是实现Into&lt;String&gt; 的一种,当您尝试在其实例上调用into 时,因为automatic dereferencing。但是,在我的代码 &amp;String 中被认为是 MyIntoString,因为我为 &amp;String 实现了这个特征。在您的代码中它不起作用,因为滥用specialization
  • 如果您阅读了我的其他问题stackoverflow.com/questions/45118060/…,您知道我实际上无法在&amp;String 上致电into。所以我不明白为什么这里会自动取消引用而不在那里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 2021-01-09
  • 2011-02-15
  • 2015-10-08
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
相关资源
最近更新 更多