String vs str Methods
String is the owned, growable UTF-8 buffer; str is the borrowed UTF-8 text slice, and String gets most str methods through deref coercion.
What it is
String owns heap allocation and can change length.
str is dynamically sized and is usually seen as &str.
Both represent valid UTF-8.
Methods that allocate, reserve, push, insert, remove, or clear belong to String.
Methods that inspect, search, trim, split, parse, or iterate text generally belong to str.
Because String dereferences to str, you can call text.trim() or text.split(',') on a String.
Because &String coerces to &str, APIs should usually accept &str for read-only text.
Because String is a byte buffer with a UTF-8 invariant, length is measured in bytes.
How it works
String::new and String::with_capacity construct owned buffers.
push_str appends a string slice.
push appends one char.
reserve, try_reserve, capacity, shrink_to, and shrink_to_fit mirror vector capacity operations in bytes.
as_str borrows text as &str.
as_bytes borrows the UTF-8 bytes.
into_bytes consumes the string and returns Vec<u8>.
String::from_utf8 validates bytes and returns Result<String, FromUtf8Error>.
str::from_utf8 validates a byte slice and returns Result<&str, Utf8Error>.
split, split_once, lines, contains, find, trim, parse, chars, and char_indices are str methods.
Use String when ownership or mutation is required; use &str when reading.
Example
fn shout(input: &str) -> String {
let mut out = String::with_capacity(input.len() + 1);
out.push_str(input.trim());
out.push('!');
out.make_ascii_uppercase();
out
}
fn main() {
let owned = String::from(" hello ");
let result = shout(&owned);
assert_eq!(result, "HELLO!");
assert_eq!(owned.trim(), "hello");
assert_eq!(String::from_utf8(vec![82, 117, 115, 116]).unwrap(), "Rust");
}Best practice
- ✅ Accept
&strwhen a function only reads text. - ✅ Return
Stringwhen a function builds or transforms owned text. - ✅ Use
String::with_capacityfor predictable builders. - ✅ Use
push_strfor string slices andpushfor one Unicode scalar value. - ✅ Use
strmethods directly onStringthrough deref coercion. - ✅ Use
from_utf8instead of unchecked conversion unless an invariant is already proven. - ✅ Treat
len()as bytes and usechars().count()only when scalar count is truly needed. - ✅ Use
char_indiceswhen you need safe byte offsets into a string.
Pitfalls
- ⚠️ Accepting
Stringby value forces callers to give up ownership; see Borrowing Strings and Slices. - ⚠️
String::lenis not a user-visible character count. - ⚠️
remove,insert,truncate,split_off, anddrainrequire character-boundary byte indexes. - ⚠️
make_ascii_uppercaseonly changes ASCII bytes, not full Unicode case mapping. - ⚠️
charsiterates Unicode scalar values, not grapheme clusters. - ⚠️
as_bytes_mutand unchecked UTF-8 constructors are unsafe because they can break the UTF-8 invariant. - ⚠️ Repeated
+concatenation in loops can allocate excessively; see Building Strings Efficiently. - ⚠️ Pattern-based search returns byte indexes, so treat them as byte offsets.
See also
std: Vec, String & Slices · String and str · Borrowing Strings and Slices · Building Strings Efficiently · Splitting Strings Without Collecting · Bytes Chars and Unicode · String Byte Indexing · Assuming String Indexes Are Characters · Vec Capacity and Growth
Sources
- Rust standard library,
std::string::String— std, https://doc.rust-lang.org/std/string/struct.String.html - Rust standard library, primitive
str— std, https://doc.rust-lang.org/std/primitive.str.html
