In Rust, String
and str
are both used to represent text, but they have some important differences in terms of ownership, mutability, and storage. Here's a breakdown of the key differences:
1. Type and Ownership:
String
:String
is an owned and heap-allocated string type. This means it owns the data it contains, and the data is stored on the heap. It is mutable, so you can modify the contents of the string.String
is a growable string type. You can append to it, change its content, and perform various operations that modify it.
str
:str
is a slice type, meaning it is a reference to some UTF-8 encoded string data. It does not own the data; it is merely a view or slice into a string that already exists somewhere in memory (typically either on the stack or in aString
object).str
is immutable. You cannot modify the data of astr
directly.- In Rust,
str
is almost always used as&str
, which is a reference to a string slice.
2. Mutability:
String
:- A
String
is mutable, meaning you can change its content, append to it, and modify it. - Example:
let mut my_string = String::from("Hello"); my_string.push_str(", world!");
- A
str
:- A
str
is immutable by definition. You cannot change the contents of astr
slice. - Example:
let s: &str = "Hello"; // s.push_str(", world!"); // This would not compile, because `s` is immutable.
- A
3. Storage:
String
:String
is heap-allocated. It dynamically grows and shrinks as needed when you add or remove characters. It stores the UTF-8 encoded string data on the heap.
str
:str
is typically stored inline (on the stack) or as part of some other data structure (such as aString
). A&str
is just a reference, not an owned value.- The data behind a
str
slice can be on the stack or on the heap, depending on where it comes from.
4. Usage:
String
:- Used when you need ownership of the string data, and you want to modify or mutate it.
- Typically,
String
is used when you need to create and manipulate strings dynamically, especially when you're unsure of the string's size or content at compile time. - Example:
let mut s = String::from("hello"); s.push_str(" world");
str
:- Used when you want to borrow a string (without taking ownership) or when you have a reference to a part of a string.
str
is often used in function signatures where you want to take a borrowed string, rather than owning the string.- Example:
fn print_str(s: &str) { println!("{}", s); } let s = String::from("hello"); print_str(&s); // Pass a reference to `String` as `&str`
5. Conversion:
- You can easily convert between
String
and&str
:- From
String
to&str
: You can borrow aString
as an immutable&str
using theas_str()
method or simply by using a reference (&
).let s = String::from("Hello"); let s_ref: &str = &s; // Borrowing `String` as `&str`
- From
&str
toString
: You can convert a&str
to aString
by calling theto_string()
method or usingString::from()
.let s: &str = "Hello"; let s_string = s.to_string(); // Or String::from(s)
- From
6. Memory and Performance:
String
:- Since
String
is heap-allocated, creating aString
is generally more expensive in terms of performance and memory usage. However,String
is flexible and suitable for dynamic operations like resizing, concatenation, etc.
- Since
str
:- A
&str
is a lightweight, non-owning reference to string data, which is very efficient when you're working with data that doesn’t need to be modified or owned. You don’t pay the cost of heap allocation, so it is faster for cases where no modification is needed.
- A
Summary Table:
Feature | String | str (or &str ) |
---|---|---|
Type | Owned, heap-allocated | Immutable, reference to data |
Mutability | Mutable | Immutable |
Storage | Heap-allocated | Stack or part of another structure |
Use case | When you need ownership and mutability | When you just need a reference |
Conversion | Can be converted to &str | Can be converted to String |
When to Use Each:
- Use
String
when you need to own, mutate, or modify a string. It's more flexible but comes with the overhead of heap allocation. - Use
&str
when you just need to reference a string, pass it around, or perform operations that don't require modifying the string itself. It’s more efficient for passing around immutable data.
Both types are essential in Rust's ownership model, and you will often find yourself using both depending on whether you need ownership or just a reference.