What are the differences between Rust's `String` and `str`?
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 theString
'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] = &[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.
I have a C++ background and I found it very useful to think about String
and &str
in C++ terms:
- A Rust
String
is like astd::string
; it owns the memory and does the dirty job of managing memory. - A Rust
&str
is like achar*
(but a little more sophisticated); it points us to the beginning of a chunk in the same way you can get a pointer to the contents ofstd::string
.
Are either of them going to disappear? I do not think so. They serve two purposes:
String
keeps the buffer and is very practical to use. &str
is lightweight and should be used to "look" into strings. You can search, split, parse, and even replace chunks without needing to allocate new memory.
&str
can look inside of a String
as it can point to some string literal. The following code needs to copy the literal string into the String
managed memory:
let a: String = "hello rust".into();
The following code lets you use the literal itself without copy (read only though)
let a: &str = "hello rust";
str
, only used as &str
, is a string slice, a reference to a UTF-8 byte array.
String
is what used to be ~str
, a growable, owned UTF-8 byte array.