Why does Rust have both String
and str
? What are the differences between them, and when should one be used over the other? Is one of them getting deprecated?
String
is the dynamic heap string type, like Vec
: use it when you need to own or modify your string data.
str
is an immutable1 sequence of UTF-8 bytes of dynamic length somewhere in memory. Since the size is unknown, one can only handle it behind a pointer. This means that str
most commonly2 appears as &str
: a reference to some UTF-8 data, normally called a "string slice" or just a "slice". A slice is just a view onto some data, and that data can be anywhere, e.g.
In static storage: a string literal "foo"
is a &'static str
. The data is hardcoded into the executable and loaded into memory when the program runs.
Inside a heap allocated String
: String
dereferences to a &str
view of the String
's data.
On the stack: e.g. the following creates a stack-allocated byte array, and then gets a view of that data as a &str
:
use std::str;
let x: [u8; 3] = [b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(&x).unwrap();
In summary, use String
if you need owned string data (like passing strings to other threads, or building them at runtime), and use &str
if you only need a view of a string.
This is identical to the relationship between a vector Vec<T>
and a slice &[T]
, and is similar to the relationship between by-value T
and by-reference &T
for general types.
1 A str
is fixed-length; you cannot write bytes beyond the end, or leave trailing invalid bytes. Since UTF-8 is a variable-width encoding, this effectively forces all str
s to be immutable in many cases. In general, mutation requires writing more or fewer bytes than there were before (e.g. replacing an a
(1 byte) with an ä
(2+ bytes) would require making more room in the str
). There are specific methods that can modify a &mut str
in place, mostly those that handle only ASCII characters, like make_ascii_uppercase
.
2 Dynamically sized types allow things like Rc<str>
for a sequence of reference counted UTF-8 bytes since Rust 1.2. Rust 1.21 allows easily creating these types.