-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drain_filter_take #3299
Comments
I think it would also be useful to be able to drain a vector by mapping a function that takes each item by value and decides whether to yield a transformed version of it or put it back, kind of like pub fn drain_map_ok<U, F>(&mut self, filter: F) -> DrainMapOk<'_, U, T, F, A>
where
F: FnMut(T) -> Result<U, T>; So all of the My specific use case for this is extracting all of the literals from a list of expressions in the optimization pass of my compiler. |
@Johan-Mi originally I wanted to call it a drain_filter_map, but turns out that removed items are already moved out in the closure, so it doesn't make sense to return an iterator here. |
oh, I see you're talking about a Result not an Option. But it'd be weird to have it Ok/Err, isn't it? I get your motivation of that mapping is the Ok variant and retaining is the Err variant, but it sounds unnecessarily complex to use. |
You're right, perhaps it would be clearer with a different return type for the callback, with enum variants named something like |
Why not use |
@the8472 That consumes the entire original vector instead of only removing certain elements. |
the collect output contains the remaining elements which you can then continue to use |
If you choose to collect the remaining elements then you'll lose the other elements—or you could do it the other way around. Either way you lose one half of the input, making it useful only for side effects. |
The proposed method |
I suppose I should turn my proposal into a separate issue, I don't have much experience with RFCs and only added it here since it seemed somewhat related. To clarify, what I'm suggesting is a different method that does return an iterator. |
@the8472 doesn't that reallocate the whole vector in a new buffer? |
Hmm... rust vectors are not linked lists, so if you want to take ownership of an element in the middle of a vector an "drain" it out if the vector, then you have to describe how the memory structure if the vector looks afterwards. One idea is to do something like swap_remove. |
The actual drain internals use some unsafe trickery where they decrease the length of the vector before creating the iterator and then use some raw reads, which seem to be guaranteed to be safe since the iterator has a mutable reference to the vector. I'm not too familiar with the details but it seems like the methods proposed by @SOF3 and I should both be doable with zero allocations. |
It's not guaranteed, but currently the reallocation is optimized away. |
A simple alternative would be just creating an enum FilterMapInnerResult<T, U> {
/// element gets put back into the vector
Keep(T),
/// element gets yielded (and maybe transformed),
/// and efficiently removed from the vector
Yield(U),
} |
Currently we have the unstable
Vec::drain_filter
:My use case is that I want to perform some side effects in
filter
, where the side effects would determine whether the item should be drained, but performing the side effects requires moving out theT
if the element is to be filtered.I propose a new method
drain_filter_take
that takes the form(I am not sure what a good name would be that clearly indicates it does not return an iterator)
If
Some
is returned, it replaces the original value; ifNone
is returned, the value is drained. This allows the filter to take ownership ofT
and only return it if not drained.The text was updated successfully, but these errors were encountered: