为什么String没有实现From<&String>?

3

背景

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

一个例子是当您调用std::iter::Iterator::peekable时。返回值是一个将原始迭代器包装起来并给您提供一个额外的方法peekPeekable<I>对象。

这里的重点是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才能使其正常工作,但我想知道原因。


2
我不会说这是反直觉的 - 其他对象也不这样做,例如 Vec<T> 不会 impl From<&Vec<T>>;而且 impl<'a> From<&'a str> 更有可能被使用,并且它已经准备就绪。 - ljedrz
我觉得真正的答案与我的相关问题有关 https://dev59.com/-KPia4cB1Zd3GeqPy3eh - Earth Engine
2个回答

4
为了解决您的问题,可以想象向标准库添加一个新的通用实现:
impl<'a, T: Clone> From<&'a T> for T { ... }

或者更通用地说:

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

然而,这样做存在两个问题:

  1. 特化: 允许重叠特质实现的特化功能仍然不稳定。设计一个可靠的特化功能比预期更加困难(主要是由于生命周期)。

    Rust 开发人员非常谨慎,不会在标准库的公共 API 中暴露它。这并不意味着它在 std 中完全没有用途!一个著名的例子是 str 的专门化 ToString 实现。它是在 此 PR 中引入的。正如您可以在 PR 的讨论中读到的那样,他们之所以接受它,是因为它不会改变 API(to_string() 已经为 str 实现了)。

    但是,如果我们添加上面的通用实现,则会改变 API。因此,它尚未被允许在 std 中使用。

  2. core vs std: 特质 FromIntocore 中定义,而 CloneToOwnedstd 中定义。这意味着我们无法在 core 中添加通用实现,因为 core 不知道有关 std 的任何信息。但是我们也无法在 std 中添加通用实现,因为通用实现需要与特质位于同一个 crate 中(这是孤儿规则的结果)。

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


请注意添加。
impl<'a> From<&'a String> for String { ... }

...运行得很好。它不需要专业知识,也不会出现孤儿规则的问题。但当然,如果通用实现有意义,我们就不希望添加特定实现。

(感谢IRC上的可爱人们为我解释事情)


1

由于 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的严格超集。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接