Text in Rust is split across two types, and the split confuses everyone at first: String and &str. The distinction is exactly the owned-vs-borrowed split you already know.
String owns; &str borrows
String is a growable, heap-allocated, owned UTF-8 buffer — you can append to it, and it frees its memory when dropped. &str (a 'string slice') is a borrowed view into UTF-8 text someone else owns — a window, not an owner. A string literal "hi" is a &str pointing into your binary.
Which to use where
Take &str in function parameters — it accepts both literals and borrowed Strings. Return or store String when you need ownership. Build a String with String::from, .to_string(), or format!; append with push_str. The &String to &str coercion happens automatically, so &str parameters 'just work' with both.
s[0] on a String — a character may be multiple bytes, so indexing by byte could split one. Iterate with .chars() for characters or .bytes() for raw bytes, and slice only on known char boundaries. Rust makes you respect Unicode.Why two types is actually simpler
It feels like overhead until you internalize it: the type tells you who owns the text. String in a struct = the struct owns its text and has no lifetime concerns. &str = a borrow tied to a source. Once owned-vs-borrowed clicks (it did in the Borrowing track), the two string types stop being confusing and start being informative.