Why does Rust have String
and str
? What are the differences between String
and str
? When does one use String
instead of str
and vice versa? Is one of them getting deprecated?
Rust doesn't sugar coat a lot of the ugliness and complexity of string handling from developers like other languages do and therefore helps in avoiding critical mistakes in the future. By construction, both string types are valid UTF-8. This ensures there are no misbehaving strings in a program.
The str type, also called a 'string slice', is the most primitive string type. It is usually seen in its borrowed form, &str . It is also the type of string literals, &'static str . String slices are always valid UTF-8.
&'static means a reference that cannot be dangling. str is a type which represents a sequence of bytes which also is valid UTF-8 sequence. It's size cannot be determined statically as there isn't single "length" for every possible text data.
Trait std::borrow::Borrow. A trait for borrowing data. In Rust, it is common to provide different representations of a type for different use cases. For instance, storage location and management for a value can be specifically chosen as appropriate for a particular use via pointer types such as Box<T> or Rc<T> .
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] = &[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
.
2Dynamically 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With