> Even more annoying is that I have to duplicate the iterator code: as I often work with frames that may be flipped bottom-up I want to write code like
>
> let mut iterator = if !flipped {
> data.chunks_mut(stride)
> } else {
> data.chunks_mut(stride).rev()
> };
> for line in iterator { ... }
>
> but I can’t since objects have different type (and probably size) and there’s no realistic way to coerce them to the same interface.
For this specific case there is a relatively ergonomic solution using dynamic trait objects + temporary lifetime extensions, available since Rust 1.79: let iterator: &mut dyn Iterator<Item = _> = if !flipped {
&mut data.into_iter()
} else {
&mut data.into_iter().rev()
};
for line in iterator { ... }
Alternatively, if this is not a feasible solution (e.g. we want to return the new iterator to an outer scope, or the overhead of dyn indirection is unacceptable in this context), one can consider using itertools::Either instead: use itertools::Either;
fn conditionally_flip_iter<I: DoubleEndedIterator>(
data: I,
flipped: bool,
) -> impl Iterator<Item = I::Item> {
if !flipped {
Either::Left(data.into_iter())
} else {
Either::Right(data.into_iter().rev())
}
}
// ...
let mut iterator = conditionally_flip_iter(data, flipped);
for line in iterator { ... }
Rust editions only (and rarely!) break your code when you decide to upgrade your project's edition. Your public API stays the same as well (IIRC), so upgrading edition doesn't break your dependents either -unless they don't have a new enough version of the compiler to support said newer edition.