-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Implement drain RFC #23071
Implement drain RFC #23071
Conversation
Closes #23055 [breaking-change]
Oh woah the RFC landed. |
@@ -553,8 +553,8 @@ impl<T: Ord> BinaryHeap<T> { | |||
#[inline] | |||
#[unstable(feature = "collections", | |||
reason = "matches collection reform specification, waiting for dust to settle")] | |||
pub fn drain(&mut self) -> Drain<T> { | |||
Drain { iter: self.data.drain() } | |||
pub fn drain<'a>(&'a mut self) -> Drain<'a, T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the lifetimes can be elided here?
Updated. |
left: *const T, | ||
right: *const T, | ||
marker1: PhantomData<&'a ()>, | ||
marker2: PhantomData<T>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I were going to write Drain
in pure safe code, it would look something like:
pub struct Drain<'a, T:'a> {
start: uint,
len: uint,
victim: &'a mut Vec<T>
}
then on each call to next
I could call remove
on the item at self.start
and decrement self.len
. When dropping I would keep calling next
self.len
times. This should be roughly equivalent to the current behavior, though the modifications to the vector happen at different times, but I believe that is unobservable.
Therefore, I believe the marker you want is PhantomData<&'a mut Vec<T>>
.
It is reasonable to want to put PhantomData<T>
, because the destructor will drop instances of T
, but not necessary -- the dropck will already be imposing conservative rules on Drain
because it contains borrowed data. Basically, if you model what you would have written in safe code, you should be ok (unless the language itself is flawed).
cc @pnkfelix
UPDATE: Fixed a reference to &'a Vec<T>
that should have been &'a mut Vec<T>
To summarize a length discussion on IRC about markers:
|
Updated. |
ps this API is pretty nifty. |
Updated. |
I believe this doesn't closr the rfc issue proper. At least VecDeque is outstanding. |
VecDeque was not part of the RFC. It talked only about Vec and String. |
cc me |
I think there was some oversight in terms of the full force of the RFC. Although the RFC was explicitly only talking about If needed, we can file an amendment to the RFC to clarify this point. |
Also, the RFC left unspecified the exact trait used for capturing the various range types (which was a point of frustration, since it's much better to specify these details up front), but I personally would strongly prefer to introduce a general trait that covers the various ranges over a given type |
By contrast I'd rather favour However I think this needs HKT. |
For reference, I picture:
|
What's stopping this PR from being merged right now? A new RFC can get a new PR and for the most common use case, calling drain with an explicitly typed range parameter, all changes will be backwards compatible. |
The elements of Vec are removed when dropping Drain, but the length of Vec
is changed before creating Drain. I'm not sure the state of the Vec is
valid in Drain's lifetime. Let me know if I'm wrong.
|
I agree, but this is all |
@liigo I think the Vec is borrowed for the lifetime.of the drain, so one cannot push onto it or otherwise cause a realloc , and it looks like the drain drop implementation will clean up its part if we hit a panic. Still, a compile-fail test for the former and a run-pass test for the latter seem like good exercises |
@gankro, you don't need HKT, I also think it is silly to implement Drain for other specifiers than ranges. Skip single indices and leave that to other plain remove methods. It's not good API design to combine different functionality. Of course constructing a range that spans just a single key will often be possible. |
Should have voiced your concerns before the RFC was accepted. |
It's not clear to me how much this design discussion concerns this PR vs the overall end point of the design. Seems like a thread on internals would be a better venue for discussing the latter. |
Implement Vec::drain(\<range type\>) from rust-lang/rfcs#574, tracking issue #23055. This is a big step forward for vector usability. This is an introduction of an API for removing a range of *m* consecutive elements from a vector, as efficently as possible. New features: - Introduce trait `std::collections::range::RangeArgument` implemented by all four built-in range types. - Change `Vec::drain()` to use `Vec::drain<R: RangeArgument>(R)` Implementation notes: - Use @gankro's idea for memory safety: Use `set_len` on the source vector when creating the iterator, to make sure that the part of the vector that will be modified is unreachable. Fix up things in Drain's destructor — but even if it doesn't run, we don't expose any moved-out-from slots of the vector. - This `.drain<R>(R)` very close to how it is specified in the RFC. - Introduced as unstable - Drain reuses the slice iterator — copying and pasting the same iterator pointer arithmetic again felt very bad - The `usize` index as a range argument in the RFC is not included. The ranges trait would have to change to accomodate it. Please help me with: - Name and location of the new ranges trait. - Design of the ranges trait - Understanding Niko's comments about variance (Note: for a long time I was using a straight up &mut Vec in the iterator, but I changed this to permit reusing the slice iterator). Previous PR and discussion: #23071
Closes #23055
[breaking-change]
r? @gankro