为什么 String 不实现 From<&String>?

Why doesn't String implement From<&String>?

背景

我知道在 Rust 中人们更喜欢 &str 而不是 &String。但在某些情况下,我们只得到 &String.

一个例子是你调用std::iter::Iterator::peekable。 return 值是一个 Peekable<I> 对象,它将原始迭代器包装到其中并为您提供一个额外的方法 peek.

这里的重点是 peek 只给你一个迭代器项的引用。因此,如果您有一个包含 String 的迭代器,那么在这种情况下您只有 &String。当然,您可以轻松地使用 as_str 来获得 &str 但在我将在下面显示的代码中,它等同于调用 clone.

问题

这个代码

#[derive(Debug)]
struct MyStruct(String);

impl MyStruct {
    fn new<T>(t: T) -> MyStruct
    where
        T: Into<String>,
    {
        MyStruct(t.into())
    }
}

fn main() {
    let s: String = "Hello world!".into();
    let st: MyStruct = MyStruct::new(&s);
    println!("{:?}", st);
}

无法编译,因为 String 未实现 From<&String>。这不直观。

为什么这不起作用?它只是标准库缺少的功能还是有一些其他原因阻止标准库实现它?

在实际代码中,我只有对 String 的引用,我知道要让它工作我只需要调用 clone,但我想知道为什么。

为了解决您的问题,可以想象向标准库添加一个新的通用实现:

impl<'a, T: Clone> From<&'a T> for T { ... }

或者让它更通用:

impl<B, O> From<B> for O where B: ToOwned<Owned=O> { ... }

但是,这样做有两个问题:

  1. Specialization:允许重叠 trait-impls 的专业化特性仍然不稳定。事实证明,以合理的方式设计专业化是方式more difficult than expected(主要是由于生命周期)。

    如果它不是稳定的,Rust 开发者会非常小心不要在标准库的 public API 的某处公开该功能。这并不意味着它根本没有在 std 中使用!一个著名的例子是 str 的专用 ToString impl。它被引入 in this PR。正如您在 PR 的讨论中看到的那样,他们只接受了它,因为它没有改变 API(to_string() 已经为 str 实现)。

    但是,当我们添加上面的通用 impl 时情况不同:它会更改 API。因此,在 std 中还不允许。

  2. core vs std:特征 FromInto 定义在 core library,而 CloneToOwnedstd 中定义。这意味着我们不能在 core 中添加泛型实现,因为 corestd 一无所知。但是我们也不能在 std 中添加泛型 impl,因为泛型 impl 需要与 trait 在同一个 crate 中(这是孤儿规则的结果)。

    因此,在能够添加这样一个通用的实现之前,它需要某种形式的重构和移动定义(这可能困难也可能不困难)。


注意添加

impl<'a> From<&'a String> for String { ... }

... 工作得很好。它不需要专业化,也没有孤儿规则的问题。但当然,当通用实现有意义时,我们不想添加特定实现。

感谢 IRC 上可爱的人们向我解释东西

由于 String 确实实现了 From<&str>,您可以进行简单的更改:

fn main() {
    let s: String = "Hello world!".into();
    // Replace &s with s.as_str()
    let st: MyStruct = MyStruct::new(s.as_str());
    println!("{:?}", st);
}

所有 &String 都可以通过 as_str 简单地转换为 &str,这就是为什么所有 API 都应该更喜欢使用 &str 的原因;它是接受 &String.

的严格超集