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

我怕爱的太早我们不能终老 提交于 2019-12-12 13:06:55

问题


Background

I know that in Rust people prefer &str rather than &String. But in some case we were only given &String.

One example is when you call std::iter::Iterator::peekable. The return value is a Peekable<I> object that wraps the original iterator into it and gives you one extra method peek.

The point here is that peek only gives you a reference to the iterator item. So if you have an iterator that contains Strings, you only have &String in this case. Of cause, you can easily use as_str to get a &str but in the code I will show below it is equivalent to a call to clone.

The question

This code

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

doesn't compile because String doesn't implement From<&String>. This is not intuitive.

Why does this not work? Is it just a missing feature of the standard library or there are some other reasons that prevent the standard library from implementing it?

In the real code, I only have a reference to a String and I know to make it work I only need to call clone instead, but I want to know why.


回答1:


To solve your problem, one could imagine adding a new generic impl to the standard library:

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

Or to make it more generic:

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

However, there are two problems with doing that:

  1. Specialization: the specialization feature that allows to overlapping trait-impls is still unstable. It turns out that designing specialization in a sound way is way more difficult than expected (mostly due to lifetimes).

    Without it being stable, the Rust devs are very careful not to expose that feature somewhere in the standard library's public API. This doesn't mean that it isn't used at all in std! A famous example is the specialized ToString impl for str. It was introduced in this PR. As you can read in the PR's discussion, they only accepted it because it does not change the API (to_string() was already implemented for str).

    However, it's different when we would add the generic impl above: it would change the API. Thus, it's not allowed in std yet.

  2. core vs std: the traits From and Into are defined in the core library, whereas Clone and ToOwned are defined in std. This means that we can't add a generic impl in core, because core doesn't know anything about std. But we also can't add the generic impl in std, because generic impls need to be in the same crate as the trait (it's a consequence of the orphan rules).

    Thus, it would required some form of refactoring and moving around definitions (which may or may not be difficult) before able to add such a generic impl.


Note that adding

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

... works just fine. It doesn't require specialization and doesn't have problems with orphan rules. But of course, we wouldn't want to add a specific impl, when the generic impl would make sense.

(thanks to the lovely people on IRC for explaining stuff to me)




回答2:


Since String does implement From<&str>, you can make a simple change:

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

All &Strings can be trivially converted into &str via as_str, which is why all APIs should prefer to use &str; it's a strict superset of accepting &String.



来源:https://stackoverflow.com/questions/45118060/why-doesnt-string-implement-fromstring

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!