When working with iterators, you often need to inspect or track the state of iteration without fundamentally changing the data. Rust provides several powerful methods for this: enumerate() adds indices to your elements, peekable() lets you look ahead without consuming, and inspect() allows side effects for debugging.
These methods are invaluable for debugging iterator pipelines, implementing parsers that need lookahead, or simply tracking positions within a sequence.
let words = vec!["hello", "world", "rust"];
// enumerate() adds indices
for (index, word) in words.iter().enumerate() {
println!("{}: {}", index, word);
}
// Output:
// 0: hello
// 1: world
// 2: rust
// peekable() allows looking ahead
let mut iter = words.iter().peekable();
while let Some(word) = iter.next() {
if let Some(&next_word) = iter.peek() {
println!("{} is followed by {}", word, next_word);
} else {
println!("{} is the last word", word);
}
}enumerate() - Wraps each element with its index as (index, element) tuplespeekable() - Creates an iterator that can look at the next element via peek() without consuming itinspect(closure) - Calls a closure on each element for side effects (e.g., debugging) without modifying the iterator// inspect() for debugging pipelines
let sum: i32 = vec![1, 2, 3, 4, 5]
.iter()
.inspect(|x| {
println!("Before filter: {}", x)
})
.filter(|x| *x % 2 == 0)
.inspect(|x| {
println!("After filter: {}", x)
})
.sum();
// Prints debugging info while computing sum = 6Implement the following functions that use iterator inspection methods:
indexed_elements<T: Clone>( items: &[T] ) -> Vec<(usize, T)>
enumerate()find_index<T: PartialEq>( items: &[T], target: &T ) -> Option<usize>
elements_with_next<T: Clone>(items: &[T]) -> Vec<(T, Option<T>)>
peekable()group_consecutive_duplicates<T: Clone + PartialEq>(items: &[T]) -> Vec<(T, usize)>
peekable()find_first_repeated<T: Clone + PartialEq>(items: &[T]) -> Option<T>
peekable()collect_with_trace<T: Clone + std::fmt::Debug>(items: &[T], trace: &mut Vec<String>) -> Vec<T>
inspect()sum_with_running_total( numbers: &[i32], totals: &mut Vec<i32> ) -> i32
inspect()// indexed_elements
assert_eq!(
indexed_elements(&["a", "b", "c"]),
vec![(0, "a"), (1, "b"), (2, "c")]
);
// find_index
assert_eq!(find_index(&[10, 20, 30, 40], &30), Some(2));
assert_eq!(find_index(&[10, 20, 30], &50), None);
// elements_with_next
assert_eq!(
elements_with_next(&[1, 2, 3]),
vec![(1, Some(2)), (2, Some(3)), (3, None)]
);
// group_consecutive_duplicates
assert_eq!(
group_consecutive_duplicates(
&[1, 1, 2, 3, 3, 3]
),
vec![(1, 2), (2, 1), (3, 3)]
);
// find_first_repeated
assert_eq!(find_first_repeated(&[1, 2, 2, 3]), Some(2));
assert_eq!(find_first_repeated(&[1, 2, 3]), None);
// collect_with_trace
let mut trace = Vec::new();
let result = collect_with_trace(&[1, 2, 3], &mut trace);
assert_eq!(result, vec![1, 2, 3]);
assert_eq!(trace, vec!["1", "2", "3"]);
// sum_with_running_total
let mut totals = Vec::new();
let sum = sum_with_running_total(
&[10, 20, 30],
&mut totals
);
assert_eq!(sum, 60);
assert_eq!(totals, vec![10, 30, 60]);indexed_elements, use
.iter().enumerate() and clone the elements
when collectingfind_index, use enumerate() with
find() to locate the target, then extract
the indexelements_with_next, use peekable()
with peek() to look at the next element
before calling next()group_consecutive_duplicates, use
peekable() to compare current with next and
count runsfind_first_repeated, use peekable()
to compare each element with its successor via
peek()collect_with_trace, use inspect() to
push formatted elements into the trace vectorsum_with_running_total, use inspect()
with a running total that gets updated and
pushedpeek() returns Option<&T>, so
you may need to handle references carefully