Working with strings in Rust

What are some good guidelines to follow when making the decision between whether you want something to be a &str or a String in Rust?

I find myself using a decent number of string literals in my code, but I often use Strings instead of &str, and I thus have to do a lot of "something".to_string() or String::from("something"). Is this pretty typical or would it be better to just prefer &str types to String? I try to stay away from &str because it places the chars in static memory and also introduces lifetime specifier requirements rather than just utilizing moves like Strings, but I'm not sure if this is a good practice.

1 Like

Use String when you want (or need) to allocate a new piece of memory for your string, or you're happy to pass ownership of that data around. Use &str when you want to pass around a view into an existing string without changing who owns the actual data.

The main places I tend to use &str over String are:

  • Function arguments - there's rarely need for a function to take ownership of a string, unless it's constructing an object or something like that.
  • In structs that operate on a string owned by something else - for example, the standard library's Chars iterator would be very inefficient if it made an entirely new copy of the string it was iterating over every time you used it!

If it helps - consider the fact that String is effectively just Vec<u8> and &str is effectively just &[u8]. All the usual rules of thumb about where to use a Vec and where to use a slice apply just as much to strings and string slices.

4 Likes
  • In function arguments you almost always want &str, because that's the most universal type and works with most types of arguments, including String (if you have String foo, then &foo can be automatically converted to &str thanks to Deref magic).

    If the function really needs a String, you can use foo: impl Into<String> as an argument and call foo.into(), and it will efficiently support both String, &str and a bunch of others.

  • In structs you almost always want String, so that you can freely pass the struct around.

    Structs tied to external references (Foo<'a>) are very restricted in what they can do, and if you're not experienced with ownership and borrowing, will cause endless borrow checker trouble.

The same applies to other pairs of owned/borrowed types in Rust, like Vec + &[…], and PathBuf + Path.

3 Likes

Don't forget Cow<'static, str> as well when sometimes you want/need a string literal and other times an owned String.