Whenever we need to use strings in Rust, we usually will need to loop over their chars and modify them in some way. Rust provides iterators over strings.
With a for-in
loop, we can use an iterator with minimal syntax to loop over a string
. We can use the loop to create a new string
, or perform other logic.
To begin, we have a simple string
that contains the word "abc." We call chars()
to get an iterator of all the characters in this string
.
for-in
loop directly on the iterator returned by chars. Each time the loop executes, the variable is set to a character.fn main() { let test = "abc"; // Get chars. let values = test.chars(); // Use for-in loop to print all chars. for c in values { println!("CHARACTER: {}", c) } }CHARACTER: a CHARACTER: b CHARACTER: c
Reverse
loopWe can change how the iterator returned by chars()
loops over characters. With rev()
, for example, we can loop over the chars in reverse order.
fn main() { let test = "cat"; // Loop over chars backwards. for c in test.chars().rev() { println!("REV: {}", c) } }REV: t REV: a REV: c
Suppose we have an ASCII-only string
, and want to get each byte
in a loop. The bytes()
function is ideal here. It returns a u8
on each iteration.
fn print_bytes(value: &str) { // Loop over bytes. for b in value.bytes() { println!("BYTE: {b}"); } } fn main() { print_bytes("rust"); }BYTE: 114 BYTE: 117 BYTE: 115 BYTE: 116
Char
vec
Suppose we want to access each character in a string
by its index. We may want to access other indexes at the same time. We can convert a string
to a Vector
.
char
. We can access adjacent chars at different indexes in the same loop.fn main() { let name = "cat"; let letters = name.chars().collect::<Vec<char>>(); // Loop over characters by index. // We can access other indexes more easily this way. for i in 0..letters.len() { println!("{}", letters[i]); } }c a t
Suppose we have a string
we know is only ASCII. Should we use chars()
or bytes()
to iterate over the string
?
chars()
function to access each char
in the string. It tests for the letter "e".bytes()
function to handle bytes.bytes()
is almost twice as fast as using chars. If a string
is only ASCII, bytes()
should be considered.use std::time::*; fn main() { let mut example = String::new(); example.push_str("yellow bird "); example.push_str("and green frog"); // Version 1: use chars. let t0 = Instant::now(); let mut count = 0; for _ in 0..1000000 { for c in example.chars() { if c == 'e' { count += 1; } } } println!("{}", t0.elapsed().as_millis()); // Version 2: use bytes. let t1 = Instant::now(); let mut count2 = 0; for _ in 0..1000000 { for b in example.bytes() { if b == b'e' { count2 += 1; } } } println!("{}", t1.elapsed().as_millis()); println!("{count} {count2}"); }20 ms (chars) 11 ms (bytes) 3000000 3000000
We cannot directly access the chars in a string
, as each char
may be a different size. But with iterators, we can loop over and test each char
in order.