As an experiment I was trying to create an iterator filter()
function that could take a set of different types of functions as filter arguments. I got half way there and ran in to some weird lifetime issues.
Consider the following code:
use std::slice::Iter;
struct Yin;
struct Yang;
// TODO: implement `filter()`
fn main() {
let slice: &[(Yin, Yang)] = &[];
let result: Vec<_> = filter(slice.iter(), |_: Yin| true).collect();
let result: Vec<_> = filter(slice.iter(), |_: Yang| true).collect();
let result: Vec<_> = filter(slice.iter(), |_: &Yin| true).collect();
let result: Vec<_> = filter(slice.iter(), |_: &Yang| true).collect();
let result: Vec<_> = filter(slice.iter(), |_: (&Yin, &Yang)| true).collect();
let result: Vec<_> = filter(slice.iter(), |_: (Yin, Yang)| true).collect();
}
I want to be be able to create the function filter()
such that the above works which requires that the following function types are accepted by filter()
.
FnMut(Yin) -> bool
FnMut(Yang) -> bool
FnMut(&Yin) -> bool
FnMut(&Yang) -> bool
FnMut((Yin, Yang)) -> bool
FnMut((&Yin, &Yang)) -> bool
I tried implementing a trait for all these closures but I quickly ran into coherence issues. So tried to do this using generics.
use std::marker::PhantomData;
use std::slice::Iter;
#[derive(Clone, Copy)]
struct Yin;
#[derive(Clone, Copy)]
struct Yang;
struct Args {
yin: Yin,
yang: Yang,
}
impl From<&Args> for Yin {
fn from(args: &Args) -> Self {
args.yin
}
}
impl From<&Args> for Yang {
fn from(args: &Args) -> Self {
args.yang
}
}
impl<'a> From<&'a Args> for (Yin, Yang) {
fn from(args: &'a Args) -> Self {
(args.yin, args.yang)
}
}
impl<'a> From<&'a Args> for &'a Yin {
fn from(args: &'a Args) -> Self {
&args.yin
}
}
impl<'a> From<&'a Args> for &'a Yang {
fn from(args: &'a Args) -> Self {
&args.yang
}
}
impl<'a> From<&'a Args> for (&'a Yin, &'a Yang) {
fn from(args: &'a Args) -> Self {
(&args.yin, &args.yang)
}
}
struct Filter<'a, F, A> {
iter: Iter<'a, (Yin, Yang)>,
f: F,
args: PhantomData<A>,
}
impl<F, A> Iterator for Filter<'_, F, A>
where
F: FnMut(A) -> bool,
for<'a> A: From<&'a Args>,
{
type Item = (Yin, Yang);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let Self { iter, f, .. } = self;
iter.find_map(move |(yin, yang)| {
let args = Args {
yin: *yin,
yang: *yang,
};
f(A::from(&args)).then(|| (*yin, *yang))
})
}
}
fn filter<'a, F, A>(iter: Iter<'a, (Yin, Yang)>, f: F) -> Filter<'_, F, A>
where
F: FnMut(A) -> bool,
A: From<&'a Args>,
{
Filter {
iter,
f,
args: PhantomData,
}
}
This works but only for the function types that take an owned argument. The problem is that the implementation bound for<'a> A: From<&'a Args>
is too strict. You can also specify the bounds like
impl<'a, F, A> Iterator for Filter<'_, F, A>
where
F: FnMut(A) -> bool,
A: From<&'a Args>,
{
// ...
}
But this doesn't compile at all. Is it possible to do what I want, maybe using a completely different way?