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

时间:2017-07-15 12:11:43

标签: rust traits

背景

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

一个例子是当你致电std::iter::Iterator::peekable时。返回值是一个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,但我想知道原因。

2 个答案:

答案 0 :(得分:4)

要解决您的问题,可以想象在标准库中添加新的通用impl:

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

或者使其更通用:

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

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

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

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

    然而,当我们添加上面的通用impl时会有所不同:它会改变API。因此,它尚未被允许进入标准状态。

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

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

  3. 请注意添加

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

    ......工作正常。它不需要专业化,也没有孤儿规则的问题。但是,当通用impl有意义时,我们当然不希望添加特定的impl。

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

答案 1 :(得分: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都可以通过&str轻松转换为as_str,这就是为什么所有API都应该首选使用&str的原因;这是接受&String的严格超集。