Strings and Text Processing

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)