Why are string literals &str instead of String in Rust?
To understand the reasoning, consider that Rust wants to be a systems programming language. In general, this means that it needs to be (among other things) (a) as efficient as possible and (b) give the programmer full control over allocations and deallocations of heap memory. One use case for Rust is for embedded programming where memory is very limited.
Therefore, Rust does not want to allocate heap memory where this is not strictly necessary. String literals are known at compile time and can be written into the
ro.data section of an executable/library, so they don't consume stack or heap space.
Now, given that Rust does not want to allocate the values on the heap, it is basically forced to treat string literals as
Strings own their values and can be moved and dropped, but how do you drop a value that is in
ro.data? You can't really do that, so
&str is the perfect fit.
Furthermore, treating string literals as
&str (or, more accurately
&'static str) has all the advantages and none of the disadvantages. They can be used in multiple places, can be shared without worrying about using heap memory and never have to be deleted. Also, they can be converted to owned
Strings at will, so having them available as
String is always possible, but you only pay the cost when you need to.
To create a
String, you have to:
- reserve a place on the heap (allocate), and
- copy the desired content from a read-only location to the freshly allocated area.
If a string literal like
"foo" did both, every string would effectively be allocated twice: once inside the executable as the read-only string, and the other time on the heap. You simply couldn't just refer to the original read-only data stored in the executable.
&str literals give you access to the most efficient string data: the one present in the executable image on startup, put there by the compiler along with the instructions that make up the program. The data it points to is not stored on the stack, what is stack-allocated is just the pointer/size pair, as is the case with any Rust slice.
"foo" desugar into what is now spelled
"foo".to_owned() would make it slower and less space-efficient, and would likely require another syntax to get a non-allocating
&str. After all, you don't want
x == "foo" to allocate a string just to throw it away immediately. Languages like Python alleviate this by making their strings immutable, which allows them to cache strings mentioned in the source code. In Rust mutating
String is often the whole point of creating it, so that strategy wouldn't work.