Strings and Text Processing
chars()
The .chars() method on str returns a std::str::Chars<'_> iterator that decodes the string's UTF-8 bytes on the fly and yields each Unicode scalar value (char) by value.
Signature and Chars<'_> Capabilities
pub fn chars(&self) -> std::str::Chars<'_>
// where Chars<'_>: Iterator<Item = char> + DoubleEndedIterator + FusedIterator
Because UTF-8 is variable-width (1..=4 bytes per char):
Chars<'_>implementsDoubleEndedIterator(allowing.rev()and.next_back()because UTF-8 continuation bytes10xxxxxxcan be scanned backward unambiguously).Chars<'_>does not implementExactSizeIteratorbecause knowing the exact number ofchars requires decoding the UTF-8 stream ($O(n)$).
UTF-8 Bytes: [ 41, C3, A9, F0, 9F, A6, 80 ]
│ └──┬──┘ └─────┬──────┘ .chars()
▼ ▼ ▼
Yields: 'A' 'é' '🦀'
Iterating Forward and Backward Over Characters
fn main() {
let word = "Aé🦀";
print!("Forward: ");
for ch in word.chars() {
print!("'{ch}' ");
}
println!();
print!("Backward: ");
for ch in word.chars().rev() {
print!("'{ch}' ");
}
println!();
}
Forward: 'A' 'é' '🦀'
Backward: '🦀' 'é' 'A'
Accessing the First, Last, or $n$-th Character
Instead of indexing s[i], use .chars().next(), .chars().next_back(), or .chars().nth(n):
| Operation | Idiomatic Expression | Time Complexity |
|---|---|---|
First char |
s.chars().next() |
$O(1)$ (decodes 1–4 leading bytes) |
Last char |
s.chars().next_back() (prefer over .last()) |
$O(1)$ (decodes 1–4 trailing bytes) |
$n$-th char (0-indexed) |
s.chars().nth(n) |
$O(n)$ (advances through $n$ characters) |
Note that calling s.chars().nth(i) inside a for i in 0..n loop is an $O(n^2)$ anti-pattern because each call starts scanning from byte 0 again. Always iterate with for ch in s.chars() or collect into a Vec<char> if repeated random access by character index is required.
Viewing the Remaining Subslice with Chars::as_str()
At any point while iterating with a Chars<'_> iterator, calling chars.as_str() returns the unconsumed remainder of the string as a &str in $O(1)$ time:
fn main() {
let mut chars = "key=value".chars();
assert_eq!(chars.next(), Some('k'));
assert_eq!(chars.next(), Some('e'));
assert_eq!(chars.next(), Some('y'));
assert_eq!(chars.next(), Some('='));
// View the remaining unconsumed slice directly as a `&str`:
println!("Remaining slice: '{}'", chars.as_str());
}
Remaining slice: 'value'