String Length in Bytes
The length of a String or &str in Rust (.len()) is always measured in UTF-8 bytes, not Unicode scalar values (chars) or user-perceived grapheme clusters.
Byte Length Across Different Scripts
Because UTF-8 uses 1 byte for ASCII characters and 2 to 4 bytes for non-ASCII characters, strings with the same visual character count can have very different byte lengths:
"rust" ──> [ 72, 75, 73, 74 ] len = 4 bytes (4 ASCII chars)
"café" ──> [ 63, 61, 66, C3, A9 ] len = 5 bytes (4 Unicode chars)
"বাংলা" ──> [ E0 A6 AC, E0 A6 BE, E0 A6 82, ... ] len = 15 bytes (5 Unicode chars)
"🦀🚀" ──> [ F0 9F A6 80, F0 9F 9A 80 ] len = 8 bytes (2 emoji chars)
fn main() {
let samples = ["rust", "café", "বাংলা", "🦀🚀"];
for s in samples {
println!(
"{:<8} -> {:>2} bytes, {} Unicode scalar values",
s,
s.len(),
s.chars().count()
);
}
}
rust -> 4 bytes, 4 Unicode scalar values
café -> 5 bytes, 4 Unicode scalar values
বাংলা -> 15 bytes, 5 Unicode scalar values
🦀🚀 -> 8 bytes, 2 Unicode scalar values
Why s.len() Returns Byte Length in $O(1)$ Time
A &str or String stores its exact byte length in its stack header (len: usize), so calling s.len() is an $O(1)$ field read that executes in a single CPU instruction. Moreover, buffer allocations, network/file I/O (Write::write), and slice range operations (&s[start..end]) all operate on byte offsets.
Querying the UTF-8 Byte Length of a Single char
To find how many bytes a single char will occupy when encoded into a String or &str, call ch.len_utf8() (which returns 1, 2, 3, or 4):
fn main() {
for ch in ['z', 'ß', 'অ', '🎯'] {
println!("'{}' -> len_utf8() = {} byte(s)", ch, ch.len_utf8());
}
}
'z' -> len_utf8() = 1 byte(s)
'ß' -> len_utf8() = 2 byte(s)
'অ' -> len_utf8() = 3 byte(s)
'🎯' -> len_utf8() = 4 byte(s)