Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Match arms that return iterators?

I've got some code that attempts to run a match where each branch can return a different type, but all of these types implement Iterator<Item=usize>.

let found: Iterator<Item = usize> = match requirements {
    Requirements::A => MatchingAs { ainternals: [] },
    Requirements::B => MatchingBs { binternals: [] },
    Requirements::C => MatchingCs { cinternals: [] },
};

return found.any(|m| m == 1)

... where MatchingAs, MatchingBs, and MatchingCs all impl std::iter::Iterator<Item = usize>.

I'm hitting a wall with the fact that the Iterator isn't sized:

    | the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=usize>`

Is there a good approach to have match arms return objects with a shared trait, and then rely (only) on the trait in processing the results?

like image 933
Bosh Avatar asked Jul 25 '26 17:07

Bosh


1 Answers

The first reflex when you want to return something that is not Sized, is to Box it (aka, put it on the heap, return a pointer):

let found: Box<Iterator<Item = usize>> = match requirements {
    Requirements::A => Box::new(MatchingAs { ainternals: [] }),
    Requirements::B => Box::new(MatchingBs { binternals: [] }),
    Requirements::C => Box::new(MatchingCs { cinternals: [] }),
};

found.any(|m| m == 1)

This is not sufficient, here, because now match will complain that you return different types: Box<MatchingAs>, Box<MatchingBs>, ...

However, Box<Concrete> can be cast to Box<Trait> whenever there is an impl Trait for Concrete, so:

let found = match requirements {
    Requirements::A => Box::new(MatchingAs { ainternals: [] }) as Box<Iterator<Item = usize>>,
    Requirements::B => Box::new(MatchingBs { binternals: [] }) as Box<Iterator<Item = usize>>,
    Requirements::C => Box::new(MatchingCs { cinternals: [] }) as Box<Iterator<Item = usize>>,
};

found.any(|m| m == 1)

There is, though, an allocation-less solution: use generics.

fn search<T: Iterator<Item = usize>>(t: T) -> bool {
    t.any(|m| m == 1)
}

and then apply that function to each branch of the match:

match requirements {
    Requirements::A => search(MatchingAs {ainternals: []}),
    Requirements::B => search(MatchingBs {binternals: []}),
    Requirements::C => search(MatchingCs {cinternals: []}),
}

The trade-off is that it's a bit closer to callback-hell, with a somewhat indirect flow.

like image 91
Matthieu M. Avatar answered Jul 28 '26 09:07

Matthieu M.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!