Step 4 of 6
Length: bytes versus characters
How long is a string? It depends on what you count:
fn main() {
let words = ["crab", "café", "日本", "🦀"];
for w in words {
println!("{w}: len {}, chars {}", w.len(), w.chars().count());
}
}
crab: len 4, chars 4
café: len 5, chars 4
日本: len 6, chars 2
🦀: len 4, chars 1
Rust strings are always stored as UTF-8, the encoding used by almost all of the web. In UTF-8 each character takes 1 to 4 bytes: English letters and digits take 1, é takes 2, 日 takes 3, and the crab emoji takes 4.
.len()counts bytes: how much memory the text uses..chars().count()counts characters:.chars()hands out the text onecharat a time, and.count()counts them..is_empty()istruewhen the string has no text at all (clippy prefers it overlen() == 0).
Which one do you want? Bytes matter for storage and protocols ("this database column holds 255 bytes"). Characters matter for people ("usernames are at most 20 characters"). For plain English text they're the same, which is exactly why the difference causes bugs: everything works until someone types José.
(One more subtlety for later: a few symbols that people see as one character, like flag emoji and accented letters typed as two parts, are made of several chars. Module 18 goes deeper.)
Looping over characters
for c in text.chars() visits each character as a char:
fn main() {
let word = "banana";
let mut count = 0;
for c in word.chars() {
if c == 'a' {
count += 1;
}
}
println!("{count}");
}
3
Common mistakes
- Indexing a string like an array.
s[0]doesn't compile:
fn main() {
let s = String::from("crab");
let first = s[0];
println!("{first}");
}
error[E0277]: the type `str` cannot be indexed by `{integer}`
Byte 0 might be only part of a character, so Rust refuses rather than guess. Use .chars() to walk the characters. (Module 18 shows slicing and other ways in.)
- Using
.len()for a "max N characters" rule. It counts bytes, so"café"looks like 5. Use.chars().count().
Your task
Write fits_limit(text, limit), which returns true when text has at most limit characters. Tests include accents and emoji, where bytes and characters differ.
Challenge 2
Write extra_bytes(s), which returns how many more bytes than characters the text uses (0 for plain English text). For example extra_bytes("café") is 1: 5 bytes, 4 characters.
Challenge 3
Write count_vowels(s), which counts the vowels a e i o u in the text, in lowercase and uppercase. For example count_vowels("Rustacean") is 4.
Previous: Joining text: format! and + Next: Changing case, searching, replacing and trimming