You can work with the either
crate to avoid boxing…
use either::Either;
fn find_item(
fp: &Foo,
criteria: impl Fn(&Foo, usize) -> bool,
search_dir_forward: bool,
) -> Option<usize> {
let n = fp.len();
let range = if search_dir_forward {
Either::Left(0..n)
} else {
Either::Right((0..n).rev())
};
for i in range {
if criteria(fp, i) {
return Some(i);
}
}
None
}
This is probably find, but in very high-performance loops, the Iterator
implementation for Either
could be problematic if used like this, because for
loops call .next
repeatedly, which means repeatedly checking for whether it’s the Left
or Right
case.
In such settings, the .for_each
-style iterator APIs may be more performant, though threading through this an early-return from the outer function can be a bit of a dance… e.g.
use either::Either;
use std::ops::ControlFlow::{Break, Continue};
fn find_item(
fp: &Foo,
criteria: impl Fn(&Foo, usize) -> bool,
search_dir_forward: bool,
) -> Option<usize> {
let n = fp.len();
let mut range = if search_dir_forward {
Either::Left(0..n)
} else {
Either::Right((0..n).rev())
};
if let Break(r) = range.try_for_each(|i| {
if criteria(fp, i) {
return Break(Some(i));
}
Continue(())
}) {
return r;
}
None
}
In this specific case, instead of an if … { return/break Some(item) }
inside of a loop, you can probably use Iterator::find
instead, too.
use either::Either;
fn find_item(
fp: &Foo,
criteria: impl Fn(&Foo, usize) -> bool,
search_dir_forward: bool,
) -> Option<usize> {
let n = fp.len();
let mut range = if search_dir_forward {
Either::Left(0..n)
} else {
Either::Right((0..n).rev())
};
range.find(|&i| criteria(fp, i))
}