rust-itertools / itertools

Extra iterator adaptors, iterator methods, free functions, and macros.
https://docs.rs/itertools/
Apache License 2.0
2.62k stars 298 forks source link

split iterator in two? #965

Closed dev-ardi closed 1 week ago

dev-ardi commented 1 week ago

The idea is to have a

let (right, left) = iter.split(|x| func(x));

which would be equivalent of

let right = iter.filter(|x| func(x));
let left = iter.filter(|x| !func(x));

but in a single pass.

scottmcm commented 1 week ago

You're looking for https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.partition or https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.partition_map

It can't be done lazily.

dev-ardi commented 1 week ago

It can't be done lazily.

Yeah you're right, I thought that it could be possible. What about something like

fn filter_callback<P, F>(self, predicate: P, callback: F) -> FilterCallback<Self, P, F>
where
    Self: Sized,
    P: FnMut(&Self::Item) -> bool,
    F: FnMut(&Self::Item)

To do something like

let iter = [1,2,3,4,5].iter().filter_callback(|x| (x % 2 == 0), |x| println!("{x} was odd!"));
scottmcm commented 1 week ago

I wouldn't bother adding that, because multi-callback things like that are usually bad.

That one would, for example, I think be better done with something like

.filter_map(|x| if x % 2 == 0 { Some(x) } else { println!("{x} was odd!"); None })
dev-ardi commented 1 week ago

Completely fair.

dev-ardi commented 1 week ago

multi-callback things like that are usually bad.

What about things like Option::map_or_else?

scottmcm commented 1 week ago

That's actually my go-to example of a horrible API, particularly because the closures are in the opposite order from what everyone expects it to be (which is even worse in the Result version).

But it also shows how things like foo.map_or_else(|| local + 2, |y| local * y) end up not working because it tried to move local twice, whereas if you just write

if let Some(y) = foo { local + 2 } else { local * y }

it works just fine, and it's more direct anyway.

Basically, whenever you have two FnOnce closures where only one can be called, it works particularly badly, because the signature doesn't have a way to communicate that "not both" fact.

It's not as bad when it's multiple closures called multiple times each, but it's still a sign that it might not be a good abstraction.