let chunks = s
.split_terminator(....)
.map(|e| if E_IS_LAST_ELEMENT() {
do_something_special()
} else {
e
}
How can I recognize the last element: E_IS_LAST_ELEMENT()?
I manage to identify the first element with a preceding .enumerate(), but since I do not know how many elements there are in total it does not help here.
I like to use the builtin iter::Peekable for any iterator adapter that needs to only look one element ahead, makes the implementation much shorter normally. Here's a playground implementing identify_first_last via iter::Peekable.
Also, if you have an ExactSizeIterator (which, unfortunately Chars is not) you can easily iterate over just the middle of the iterator and take the first and last elements separately
fn main() {
let mut iter = &mut b"abc".iter();
let len = iter.len();
assert!(len >= 2);
println!("first {:?}", iter.next().unwrap());
for element in iter.take(len - 2) {
println!("{:?}", element);
}
println!("last {:?}", iter.next().unwrap())
}
I repeat your code here for others to find it more easily:
//! This module contains general helper traits.
use std::{ iter, mem };
/// This trait defines the iterator adapter `identify_first_last()`.
/// The new iterator gives a tuple with an `(element, is_first, is_last)`.
/// `is_first` is true when `element` is the first we are iterating over.
/// `is_last` is true when `element` is the last and no others follow.
pub trait IdentifyFirstLast: Iterator + Sized {
fn identify_first_last(self) -> Iter<Self>;
}
/// Implement the iterator adapter `identify_first_last()`
impl<I> IdentifyFirstLast for I where I: Iterator {
fn identify_first_last(self) -> Iter<Self> {
Iter(true, self.peekable())
}
}
/// A struct to hold the iterator's state
/// Our state is a bool telling if this is the first element.
pub struct Iter<I>(bool, iter::Peekable<I>) where I: Iterator;
impl<I> Iterator for Iter<I> where I: Iterator {
type Item = (bool, bool, I::Item);
/// At `next()` we copy false to the state variable.
/// And `peek()` adhead to see if this is the last one.
fn next(&mut self) -> Option<Self::Item> {
let first = mem::replace(&mut self.0, false);
self.1.next().map(|e| (first, self.1.peek().is_none(), e))
}
}
#[test]
fn test_iterator_adaptor_identify_first_last(){
let mut iter = "abcde".chars().identify_first_last();
assert_eq!(iter.next(), Some((true, false, 'a')));
assert_eq!(iter.next(), Some((false, false, 'b')));
assert_eq!(iter.next(), Some((false, false, 'c')));
assert_eq!(iter.next(), Some((false, false, 'd')));
assert_eq!(iter.next(), Some((false, true , 'e')));
let mut iter = "a".chars().identify_first_last();
assert_eq!(iter.next(), Some((true, true, 'a')));
}
Thanks. I really like the point that we can extend the standard library whenever we think it is lacking something. This is sort of like subtyping but is more loosely written.