Question
In Rust, why are there both String and str types? What is the difference between them, when should each one be used, and is either type being deprecated?
Short Answer
By the end of this page, you will understand how Rust represents text with both String and str, why they are different types, and how ownership and borrowing affect string handling. You will also learn when to use String, when to use &str, and why neither type is being deprecated.
Concept
Rust has multiple string-related types because they serve different purposes in Rust's ownership system.
Stringis an owned, growable UTF-8 text buffer.stris a string slice type, usually used as&str, which is a borrowed view into UTF-8 text.
Why Rust needs both
In Rust, ownership matters. Sometimes your code needs to own text so it can store it, modify it, or return it from a function. Other times, your code only needs to read some text temporarily without taking ownership.
Having separate types makes these cases explicit:
- Use
Stringwhen your code owns the text. - Use
&strwhen your code just borrows text.
This helps Rust prevent memory errors and unnecessary copying.
String
String stores text on the heap and owns that memory. Because it owns the data, it can:
- grow or shrink
- be modified
- be moved between variables
- be returned from functions as owned data
Example:
let mut name = String::from("Rust");
name.push_str(" language");
Mental Model
Think of String as a book you own, and &str as a page reference or excerpt from a book.
- If you own the whole book and can add or remove pages, that is like
String. - If you are only pointing to some text inside a book owned elsewhere, that is like
&str.
Another way to see it:
String= the actual storage container&str= a window looking into text
The window does not own the text. It only lets you read it while the original text still exists.
Syntax and Examples
Core syntax
let owned: String = String::from("hello");
let borrowed: &str = "hello";
Create a String
let a = String::from("hello");
let b = "hello".to_string();
Both create owned strings.
Use a string slice
let name: &str = "Ada";
This borrows a string literal.
Borrow a String as &str
let message = ::();
: & = &message;
Step by Step Execution
Consider this example:
fn print_length(text: &str) {
println!("{} has length {}", text, text.len());
}
fn main() {
let s = String::from("hello");
let slice = &s;
print_length(slice);
}
Step by step:
String::from("hello")creates an ownedStringon the heap.sowns that string data.let slice = &s;borrowssas&str.slicedoes not copy the text. It only points to the same text.print_length(slice)passes that borrowed view into the function.- Inside
print_length, the function can read the text but does not own it. - When the function ends,
sliceis still valid because still exists.
Real World Use Cases
Accepting user input
When reading input from a user, you often store it in a String because the program owns that text and may modify it.
let mut input = String::new();
API and function parameters
Functions that only read text commonly accept &str:
fn is_valid_email(email: &str) -> bool {
email.contains('@')
}
This works with both literals and String values.
Building output text
When constructing messages, file contents, SQL queries, or JSON snippets, you usually return or build a String.
Parsing and processing text
Parsers often receive &str because they inspect text without needing to own it.
Configuration values
A config loader may temporarily borrow text as &str while validating, then store selected values as if they must live independently.
Real Codebase Usage
In real Rust codebases, developers follow a few common patterns.
Prefer &str for read-only parameters
fn log_message(message: &str) {
println!("LOG: {}", message);
}
This avoids unnecessary allocations and makes the function easier to call.
Use String for stored data
Struct fields often use String when the struct must own the text:
struct User {
username: String,
email: String,
}
Convert at boundaries
A common pattern is:
- accept
&str - validate or inspect it
- convert to
Stringonly if ownership is needed
fn create_user(username: &str) -> User {
User {
username: username.to_string(),
email: ::(),
}
}
Common Mistakes
Mistake 1: Thinking str and &str are the same thing
str is the slice type itself, but you almost always work with it through a reference: &str.
Broken idea:
let text: str = "hello";
Correct:
let text: &str = "hello";
Mistake 2: Using String for every function parameter
Broken:
fn greet(name: String) {
println!("Hello, {}", name);
}
This forces callers to give ownership.
Better:
fn greet(name: &str) {
println!(, name);
}
Comparisons
| Concept | String | &str |
|---|---|---|
| Ownership | Owns the text | Borrows the text |
| Size | Growable | Fixed view into existing text |
| Mutability | Can be modified if variable is mut | Cannot modify underlying text through &str |
| Allocation | Usually heap-allocated | Does not allocate by itself |
| Common use | Store, build, return text | Read-only parameters and views |
| Typical function argument | Less flexible | More flexible |
String vs &str in function design
Cheat Sheet
Quick rules
String= owned, growable UTF-8 textstr= string slice type&str= borrowed view into UTF-8 text- String literals are usually
&'static str - Neither
Stringnorstris deprecated
Most common choices
fn read_only(text: &str) {}
fn creates_text() -> String { String::from("hi") }
Convert between them
let s: String = String::from("hello");
let slice: &str = &s;
let owned: String = slice.to_string();
FAQ
Why does Rust have both String and str?
Because Rust separates owned data from borrowed data. String owns text, while &str borrows text.
Should I use String or &str for function parameters?
Usually &str, unless the function must take ownership of the text.
Is str ever used without &?
Rarely in everyday Rust code. Most of the time you use &str.
Are string literals String values?
No. String literals are &'static str.
Is String just a wrapper around str?
You can think of String as owned string storage that can be borrowed as &str, but they are distinct types with different roles.
Is either or deprecated in Rust?
Mini Project
Description
Build a small Rust program that manages a display name. This project demonstrates when to accept &str, when to store String, and how to convert borrowed text into owned text only when needed.
Goal
Create a program that validates a name from borrowed input and stores it as owned text in a struct.
Requirements
[
"Create a struct that stores a user's display name as a String.",
"Write a function that accepts &str input and rejects empty or whitespace-only names.",
"Trim valid input and store the cleaned value as a String.",
"Print the saved display name to confirm the result."
]
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Default Function Arguments in Rust: What to Use Instead
Learn how Rust handles default function arguments, why they are not supported, and practical patterns to achieve similar behavior.
Fixing Rust "linker 'cc' not found" on Debian in WSL
Learn why Rust shows "linker 'cc' not found" on Debian in WSL and how to fix it by installing the required C build tools.