Strings in Rust
Strings in Rust are more complex than in many other languages due to
Rust's focus on safety and UTF-8 encoding.
In this lesson, you'll learn about the two main string types
(String and
&str), how to create and manipulate strings, and common pitfalls to
avoid.
String vs &str
Rust has two main string types:
| Feature | String | &str |
|---|---|---|
| Storage | Heap-allocated | Stack or binary (immutable) |
| Ownership | Owned | Borrowed (reference) |
| Mutability | Can be mutable | Always immutable |
| Size | Growable | Fixed size |
| Use case | When you need to own/modify | String literals, slices |
&str is a string slice - a view into
string data stored elsewhere.
String is an owned, growable string type.
Click Run to execute your code
Creating Strings
There are several ways to create strings in Rust:
// String literals (&str) - hardcoded in binary
let greeting = "Hello, World!";
// String::from() - creates owned String
let s1 = String::from("Hello");
// .to_string() method - converts &str to String
let s2 = "World".to_string();
// String::new() - creates empty String
let mut s3 = String::new();
s3.push_str("Hello");
&str for string literals and function
parameters (more flexible).
Use String when you need to own or modify the string.
UTF-8 Encoding
Rust strings are always valid UTF-8. This means:
- Each character can be 1-4 bytes
- You cannot index strings directly (no
s[0]) .len()returns byte count, not character count- Emoji and international characters are fully supported
let hello = "Hello";
println!("Length: {} bytes", hello.len()); // 5 bytes
let emoji = "Hello 👋";
println!("Length: {} bytes", emoji.len()); // 10 bytes (👋 is 4 bytes!)
println!("Chars: {}", emoji.chars().count()); // 7 characters
s[0] is not allowed
in Rust because characters
can be multiple bytes. Use .chars() or .bytes() to
iterate.
String Methods
Rust provides many useful methods for working with strings:
| Method | Description | Example |
|---|---|---|
push(char) |
Add a character | s.push('!') |
push_str(&str) |
Add a string slice | s.push_str(" World") |
len() |
Get byte length | s.len() |
is_empty() |
Check if empty | s.is_empty() |
contains(&str) |
Check substring | s.contains("Rust") |
replace(&str, &str) |
Replace substring | s.replace("old", "new") |
Click Run to execute your code
String Concatenation
There are several ways to combine strings:
Using the + Operator
let s1 = String::from("Hello");
let s2 = String::from("World");
let s3 = s1 + " " + &s2; // s1 is moved here!
// println!("{}", s1); // Error: s1 was moved
+ operator takes ownership of
the left operand.
Use & for the right operand to borrow it.
Using the format! Macro
let s1 = String::from("Hello");
let s2 = String::from("World");
let s3 = format!("{} {}", s1, s2); // s1 and s2 still valid!
println!("{}", s1); // OK: s1 not moved
format! for concatenation when
you need to keep ownership
of all strings. It's more flexible and doesn't move values.
String Slicing
You can create string slices using range syntax:
let s = String::from("Hello, World!");
let hello = &s[0..5]; // "Hello"
let world = &s[7..12]; // "World"
println!("{} {}", hello, world);
let emoji = "👋";
// let bad = &emoji[0..1]; // PANIC! 👋 is 4 bytes
let good = &emoji[0..4]; // OK: full character
Iterating Over Strings
Rust provides two main ways to iterate over strings:
By Characters
for c in "Hello".chars() {
println!("{}", c);
}
// Output: H e l l o
By Bytes
for b in "Hello".bytes() {
println!("{}", b);
}
// Output: 72 101 108 108 111 (ASCII values)
- Use
.chars()when working with Unicode characters - Use
.bytes()when working with raw byte data
Common Mistakes
1. Trying to index strings
Wrong:
let s = String::from("Hello");
let c = s[0]; // Error: cannot index into a string
Correct:
let s = String::from("Hello");
let c = s.chars().nth(0).unwrap(); // Get first character
2. Confusing byte length with character count
Problem:
let s = "Hello 👋";
println!("{}", s.len()); // 10 bytes, not 7 characters!
Solution:
let s = "Hello 👋";
println!("Bytes: {}", s.len());
println!("Chars: {}", s.chars().count());
3. Moving strings unintentionally
Wrong:
let s1 = String::from("Hello");
let s2 = s1 + " World";
println!("{}", s1); // Error: s1 was moved
Correct:
let s1 = String::from("Hello");
let s2 = format!("{} World", s1);
println!("{}", s1); // OK: s1 still valid
Exercise: String Manipulation
Task: Fix the code to make it compile and run correctly.
Requirements:
- Create mutable and immutable strings
- Concatenate strings without moving ownership
- Extract substrings safely
- Count characters correctly (not bytes)
- Convert strings to uppercase
Click Run to execute your code
Show Solution
fn main() {
// Create a mutable String
let mut language = String::from("Rust");
// Add to the string
language.push_str(" is awesome!");
println!("{}", language);
// Concatenate without moving
let first = String::from("Hello");
let second = String::from("World");
let combined = format!("{} {}", first, second);
println!("{}", combined);
println!("First: {}, Second: {}", first, second);
// Extract first word
let sentence = "Rust programming language";
let first_word = &sentence[0..4]; // "Rust"
println!("First word: {}", first_word);
// Count characters
let emoji_text = "Hello 👋 World 🌍";
let char_count = emoji_text.chars().count();
println!("Character count: {}", char_count);
// Convert to uppercase
let message = "rust is great";
println!("Uppercase: {}", message.to_uppercase());
}
Summary
- Two string types:
String(owned, heap) and&str(borrowed, slice) - UTF-8 encoding: All Rust strings are valid UTF-8
- No indexing: Cannot use
s[i]due to variable-width characters - String methods:
push,push_str,len,contains, etc. - Concatenation: Use
+(moves left operand) orformat!(doesn't move) - Slicing: Must occur at UTF-8 character boundaries
- Iteration: Use
.chars()for characters,.bytes()for bytes .len()returns bytes,.chars().count()returns characters
What's Next?
Now that you understand strings, you're ready to learn about
type conversion.
In the next lesson, you'll learn how to convert between different types using
the as keyword,
From/Into traits, and safe conversion methods.
Enjoying these tutorials?