Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd Vec::drain_filter #43245
Conversation
rust-highfive
assigned
sfackler
Jul 15, 2017
This comment has been minimized.
This comment has been minimized.
|
r? @sfackler (rust_highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
|
In case you're curious here's the other impl: https://gist.github.com/Gankro/ec6bf4f939b40ace16790447ed75152b It basically does a ton of work to try to use ptr::copy() on contiguous blocks of elements instead of swapping them individually. As a result it ends up having to handle boundary cases, and ZSTs all over the place. |
This comment has been minimized.
This comment has been minimized.
|
I think this is a good thing to include in the standard library. Even though out of tree implementation may be possible, I've found multiple times that |
This comment has been minimized.
This comment has been minimized.
|
I believe this is called |
This comment has been minimized.
This comment has been minimized.
|
Neither So the three fundamentally similar operations we have are drain, retain, and filter.
This gives us a few possibilities:
The most important point, to me, is that filter and retain have reversed "polarity" when applied to this problem:
This is precisely why I didn't like drain_by/drain_with; they left the predicate ambiguous on their own. Ultimately I felt that Using bare filter as a name didn't sit well with me; we don't add iterator-ish methods directly on collections. So ultimately drain_filter seemed like the clearest name. It also, hopefully, makes it easier to find. |
Mark-Simulacrum
added
the
S-waiting-on-review
label
Jul 17, 2017
alexcrichton
added
the
T-libs
label
Jul 17, 2017
sfackler
reviewed
Jul 19, 2017
| /// But `drain_filter` is easier to use. `drain_filter` is also more efficient, | ||
| /// because it can backshift the elements of the array in bulk. | ||
| /// | ||
| /// Note that `drain_filter` also lets you mutate ever element in the filter closure, |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
This seems like a reasonable API to me. @rfcbot fcp merge |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jul 19, 2017
•
|
Team member @sfackler has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
the
proposed-final-comment-period
label
Jul 19, 2017
Gankro
force-pushed the
Gankro:drain-filter
branch
from
491f9e4
to
1af4226
Jul 19, 2017
This comment has been minimized.
This comment has been minimized.
|
typo fixed |
This comment has been minimized.
This comment has been minimized.
|
Interestingly, |
This comment has been minimized.
This comment has been minimized.
|
In my experiments iteraterification had little to no performance impact, but I don't 100% trust my results. Note that drain_filter has a reversed predicate to retain_mut. |
carols10cents
added
S-waiting-on-team
and removed
S-waiting-on-review
labels
Jul 24, 2017
This comment has been minimized.
This comment has been minimized.
dtolnay
reviewed
Jul 30, 2017
| /// let val = vec.remove(i); | ||
| /// // your code here | ||
| /// } | ||
| /// i += 1; |
This comment has been minimized.
This comment has been minimized.
dtolnay
Jul 30, 2017
Member
This would skip every item after a removed item.
let some_predicate = |&x| x == 2;
let mut vec = vec![1, 2, 2, 3, 4, 5];
let mut i = 0;
while i != vec.len() {
if some_predicate(&mut vec[i]) {
let val = vec.remove(i);
println!("removed {}", val);
}
i += 1;
}
println!("{:?}", vec);
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nrxus
commented
Jul 31, 2017
•
|
Do we really need a whole new struct that implements Iterator for this? Would it not be easier to re-use most of the retain function, and call drain after we have done the moving of the elements?
The method above is very similar to the current retain method, and re-uses the existing Drain iterator instead of creating an entire new type. |
clarfon
reviewed
Jul 31, 2017
| type Item = T; | ||
|
|
||
| fn next(&mut self) -> Option<T> { | ||
| unsafe { |
This comment has been minimized.
This comment has been minimized.
clarfon
Jul 31, 2017
Contributor
I would personally not make the unsafe cover the entire block, just the raw_parts_mut method.
This comment has been minimized.
This comment has been minimized.
Gankro
Aug 1, 2017
Author
Contributor
It's also needed for the ptr::read. At that point might as well just say the whole thing is unsafe and call it a day (this matches many parts of this file).
This comment has been minimized.
This comment has been minimized.
|
@nrxus iterators are maximally flexible and composable, and should only be avoided when there's a performance or ergonomics reason. In this case I don't think there is. |
This comment has been minimized.
This comment has been minimized.
nrxus
commented
Aug 1, 2017
|
@Gankro Drain is also an iterator though. What do we gain from a new type that implements iterator (FilterDrain) that outweights the benefit of reusing an existing working type that also implements iterator (Drain). |
This comment has been minimized.
This comment has been minimized.
|
Oh I see, I misread your original comment. I don't consider unifying DrainFilter and Drain to eliminate a struct to be intrinsically valuable to our users (and indeed, it would corner us into a very specific implementation). The maintenance burden is also fairly minimal for std. I am also concerned that this implementation has worse cache behaviour when most elements need to be removed, as it runs over the entire array an extra time to actually yield the elements. It also requires actually swapping the elements, as opposed to just memcopying one over the other. |
This comment has been minimized.
This comment has been minimized.
|
(I checked off @brson's box due to him being away) |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 1, 2017
|
|
rfcbot
added
final-comment-period
and removed
proposed-final-comment-period
labels
Aug 1, 2017
This comment has been minimized.
This comment has been minimized.
|
One problem with all these closure-based design is that you cannot use |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 11, 2017
|
The final comment period is now complete. |
This comment has been minimized.
This comment has been minimized.
|
Ping @sfackler? FCP completed without much concern besides #43245 (comment) (OP's reply at #43245 (comment)). |
carols10cents
added
S-waiting-on-review
and removed
S-waiting-on-team
labels
Aug 14, 2017
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
\o/ |
This comment has been minimized.
This comment has been minimized.
bors
added a commit
that referenced
this pull request
Aug 15, 2017
This comment has been minimized.
This comment has been minimized.
|
|
Gankro commentedJul 15, 2017
This implements the API proposed in #43244.
So I spent like half a day figuring out how to implement this in some awesome super-optimized unsafe way, which had me very confident this was worth putting into the stdlib.
Then I looked at the impl for
retain, and was like "oh dang". I compared the two and they basically ended up being the same speed. And theretainimpl probably translates to DoubleEndedIter a lot more cleanly if we ever want that.So now I'm not totally confident this needs to go in the stdlib, but I've got two implementations and an amazingly robust test suite, so I figured I might as well toss it over the fence for discussion.