Difference Between String and str in Rust
Alex Garella
18 March 2023
In Rust, there are two main string types: String and str.
The primary differences between them are in ownership, mutability, and how they are stored in memory.
String
- Growable, mutable, owned, heap-allocated string type.
- Represented as a
Vec<u8>under the hood, so it can be resized.
Usage
- When you need a modifiable string, e.g., appending, inserting, or removing characters.
- When the size of the string is unknown or may change during runtime.
- When you want to pass ownership of the string to another function or return it from a function.
Downsides
- Less efficient when dealing with constant or fixed-length strings due to heap allocation and resizing operations.
- Potential performance overhead due to heap allocation and deallocation.
Code example
fn main() {
let mut s = String::from("Hello"); // Create a new String with content "Hello"
s.push_str(", world!"); // Append the string ", world!" to the original String
println!("{}", s); // Output: "Hello, world!"
} str
- Immutable, borrowed, fixed-length, stack- or heap-allocated string slice.
- Often seen in its borrowed form, &str.
Usage
- When you need a constant, fixed-length string or a read-only view of a String.
- When you want to avoid heap allocation for better performance.
- When you want to create a function that accepts both String and &str by using &str as the input type.
Downsides
- Cannot be modified, so it’s not suitable for cases where you need to change the string.
- Can lead to lifetime-related issues when borrowing from other string types, requiring explicit lifetime annotations in some cases.
Code example
fn main() {
let s: &str = "Hello"; // Create a new &str with content "Hello"
// The following line would produce a compile-time error since &str is immutable:
// s.push_str(", world!");
println!("{}", s); // Output: "Hello"
} Converting between String and &str
Here is a code example of how to convert between String and &str
fn main() {
let s = String::from("Hello"); // Create a new String with content "Hello"
let s_slice: &str = &s; // Create a &str slice that borrows from the String
println!("String: {}", s); // Output: "Hello"
println!("&str: {}", s_slice); // Output: "Hello"
} Summary
- Use
Stringwhen you need a mutable, growable string. - Use
str(usually in its borrowed form,&str) when you need an immutable, fixed-length string.