Two String Types
fn main() {
// &str: immutable slice (often 'static - points to binary)
let s1: &str = "hello";
// String: owned, heap-allocated, mutable
let mut s2 = String::from("hello");
s2.push(' ');
s2.push_str("world");
s2 += "!";
println!("{}", s2); // "hello world!"
// format! - safe concatenation, does not move
let a = String::from("Hello");
let b = String::from("World");
let c = format!("{}, {}!", a, b); // a and b still valid
println!("{}", c);
// Conversions
let owned: String = s1.to_string(); // &str -> String
let slice: &str = &s2; // String -> &str (deref coercion)
// Prefer &str in function parameters:
fn greet(name: &str) { println!("Hello, {}!", name); }
greet("literal"); // works
greet(&s2); // &String -> &str coercion works
}String Methods and UTF-8
fn main() {
let text = " Hello, World! ";
println!("{}", text.trim());
println!("{}", text.to_lowercase());
println!("{}", text.contains("World"));
println!("{}", text.replace("World", "Rust"));
println!("{:?}", text.split(',').collect::>());
println!("{}", "ha".repeat(3)); // "hahaha"
// UTF-8 indexing - cannot use s[0]!
let emoji = "Hello, 🚀!";
println!("bytes: {}", emoji.len()); // 11 (rocket is 4 bytes)
println!("chars: {}", emoji.chars().count()); // 9
// Safe char access:
let third: Option = emoji.chars().nth(2);
println!("{:?}", third); // Some('l')
// Parse strings
let n: i32 = "42".parse().unwrap();
let f: f64 = "3.14".parse().unwrap();
let bad: Result = "oops".parse();
println!("{} {} {:?}", n, f, bad);
// Split and rejoin
let words: Vec<&str> = "one two three".split_whitespace().collect();
println!("{}", words.join("-")); // "one-two-three"
} 🎯 Practice
- Write fn is_palindrome(s: &str) -> bool using chars().collect into Vec, then compare with reversed
- Write fn word_frequency(s: &str) -> HashMap<&str, usize> counting occurrences
- Write fn title_case(s: &str) -> String capitalizing first letter of each word
🎉 Key Takeaways
- &str is a slice; String is owned. Prefer &str in function params
- Strings are UTF-8; use .chars() not byte indexing
- format!() is the clean way to concatenate without moving
- .parse::<T>() converts strings to typed values, returns Result