Current guidance on &str to String (to_owned vs to_string)

I'm looking for current guidance on the most appropriate way to convert &str to owned String. Years-old guidance (for example: Converting &str: to_string vs to_owned (with two benchmarks) | by Eric Opines | Medium) seemed to say .to_owned() was preferable for performance reasons, but I'm not seeing anything current.

Is there 2022 guidance on that front?

I usually prefer the .to_string() method because I like the name. I know others prefer the name of .to_owned() instead. They all just call each other, so their performance should be the same.

1 Like

As far as I know, all of the following should be basically equivalent in terms of performance, and the decision will come down to personal style:

  • s.to_owned()
  • s.to_string()
  • s.into()
  • String::from(s)

I would be a bit wary about anything more complicated, such as format!("{s}").

5 Likes

from() calls to_owned(); into() calls from().
to_string() (specialized) calls to_owned(), too.

So yes, all will be equal in perf.

I prefer to_owned() because it represents the intention the best: I want to go from a borrowed &str to an owned String. But I know others has other preferences.

3 Likes

From the source code, you can see that str::to_string calls String::from, which in turn calls str::to_owned, which finally calls String::from_utf8_unchecked. They'll probably all compile down to the same code.

I usually use to_string, for no particular reason a part from that I think "I want a String" and my fingers type "to_string".

1 Like

Right, the reason why folks (rightly) recommended against to_string() in the past is because it went through the formatting machinery. Back then, we didn't have specialization at all, so all we had was the blanket impl on ToString for all T: std::fmt::Display. So even for &str, it would need to go through the formatting machinery.

But not too long after Rust 1.0, we got specialization and this was one of the first things we used it: to "fix" the ToString impl for str so that it did the obvious and cheapest thing.

Sadly, specialization never got fully stabilized, but it's used in a smattering of places inside of std to speed things like this up.

Therefore, today, to_string() and to_owned() and String::from are all equivalent from a perf perspective. I mostly see folks using to_string() (including myself), but I don't think any choice is particularly wrong.

11 Likes

Some of the 2015 guidance is out of date, but I don't think anything's changed substantially in the last few years here.

I still like this post from 2017 about it:

Here're some other previous conversations:

Personally, I distinguish between to_owned and clone more than I think most people do. I like using to_owned for all &T -> T things, even where clone would work, and instead keep clone for what I think of as T -> (T, T) things (even though Clone in implementation is also &T -> T).

As such, there are times when I'd use all of the options, depending what I'm doing:

  • If I already have a String, but need another one for a bit because I have to change it to give to something else, I'd .clone() it.
  • If I have a &str (or &String if that happened from an iterator adapter or something) that I need to edit, I'll use .to_owned() on it (same as I would if I had a &[u8] and wanted a Vec).
  • If something takes a name: String then I'll use .to_string() because the fact that it's a &str I'm starting with isn't important -- if it was a Uuid or u64 or something I'd do the same.
  • If I'm calling something else and don't really care, I'd likely just use .into(), like foo(s.into()), same as I'd do if I happen to have a u32 and it wants a u64 or something like that.

But I wouldn't say that's the modern convention, just my taste.

8 Likes