Skip to content
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 Vec::drain from RFC 574 #24781

Merged
merged 2 commits into from
Apr 29, 2015
Merged

Conversation

bluss
Copy link
Member

@bluss bluss commented Apr 24, 2015

Implement Vec::drain() 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

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see CONTRIBUTING.md for more information.

@bluss bluss force-pushed the vec-drain-range branch from a6a721a to aa9e061 Compare April 24, 2015 20:13
@brson
Copy link
Contributor

brson commented Apr 24, 2015

r? @aturon

@rust-highfive rust-highfive assigned aturon and unassigned brson Apr 24, 2015
Range,
RangeTo,
RangeFrom
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistically the compiler doesn't tend to put an import-per-line like this but instead use multiple use statements.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line with {RangeFull, Range, ..} for all the ranges would be the usual style then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! The wrapping (to a new import) just happens if it runs over 80 chars

@bluss bluss force-pushed the vec-drain-range branch from 311a374 to bb12d20 Compare April 25, 2015 13:52
@bluss
Copy link
Member Author

bluss commented Apr 25, 2015

I've addressed all review comments, thank you @alexcrichton!

Introducing this as unstable gives us leeway to fix it up later, what we could change:

  • Range trait method fn into_endpoints(self) -> (Option<T>, Option<T>) would allow interpreting usize as a n..n+1 range, but it feels bad to affix that interpretation in a general trait
  • Gankro's preferred Drain trait in the previous PR would allow to do what we do with the Index trait -- each collection determines itself which ranges and index types to handle for Drain.
  • Personally I don't like .drain(usize) because I want the API to always be range based.
  • We may consider calling the method .remove_range instead. Depends on if you want to emphasize the removal feature or the iterator feature.

use core::option::Option::{self, None, Some};
use core::ops::{RangeFull, Range, RangeTo, RangeFrom};

#[unstable(feature = "collections", reason = "was just added")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second though, could you apply just one of these stability attributes to the module itself? The instability should be inherited. Additionally, could you give this a name like collections_range as well?

@alexcrichton
Copy link
Member

Looks great to me, thanks @bluss! After some minor refinements of the feature names here I think this is good to go!

@bluss
Copy link
Member Author

bluss commented Apr 27, 2015

I'm excited. I'm working on checking that it compiles and then squashing it together.

@bluss bluss force-pushed the vec-drain-range branch from be84f11 to 6e5f8ce Compare April 27, 2015 16:43
@alexcrichton
Copy link
Member

Thanks! Lemme know when it's ready to go and I'll r+

@bluss bluss force-pushed the vec-drain-range branch from 6e5f8ce to b7d1ad9 Compare April 27, 2015 17:34
RangeArgument is introduced as unstable under the
feature(collections_range)
@bluss bluss force-pushed the vec-drain-range branch from b7d1ad9 to 447ef06 Compare April 27, 2015 17:37
@bluss
Copy link
Member Author

bluss commented Apr 27, 2015

There, it should be ready. I could have missed something, haven't added new feature flags before.

@alexcrichton
Copy link
Member

@bors: r+ 447ef06

Thanks!

@bors
Copy link
Contributor

bors commented Apr 28, 2015

⌛ Testing commit 447ef06 with merge dc19940...

@bors
Copy link
Contributor

bors commented Apr 28, 2015

💔 Test failed - auto-mac-64-opt

Old `.drain()` on vec is performed using `.drain(..)` now.

`.drain(range)` is unstable and under feature(collections_drain)

[breaking-change]
@bluss bluss force-pushed the vec-drain-range branch from 447ef06 to b475fc7 Compare April 28, 2015 09:39
@bluss
Copy link
Member Author

bluss commented Apr 28, 2015

Fixed that test.

@alexcrichton
Copy link
Member

@bors: r+ b475fc7

@bors
Copy link
Contributor

bors commented Apr 28, 2015

⌛ Testing commit b475fc7 with merge 8871c17...

bors added a commit that referenced this pull request Apr 28, 2015
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
@bors bors merged commit b475fc7 into rust-lang:master Apr 29, 2015
@bluss bluss deleted the vec-drain-range branch April 29, 2015 00:18
@bors bors mentioned this pull request Apr 29, 2015
@Stebalien
Copy link
Contributor

So I know I'm starting to sound like a broken record but... RangeArgument isn't going to play well with inclusive ranges. Would you consider changing it to something like Bounds (sometime before stabilization)?

use std::collections::Bound;
trait Bounds {
    fn start(&self) -> Bound<&T>;
    fn end(&self) -> Bound<&T>;
}
impl<T> Bounds for Range<T> {
    fn start(&self) -> Bound<&T> { Bound::Included(&self.start) }
    fn start(&self) -> Bound<&T> { Bound::Excluded(&self.end) }
}
// ...

I suggest adding an into_bounds(self) -> (Bound<T>, Bound<T>) method for extracting the endpoints by value.

@bluss
Copy link
Member Author

bluss commented Apr 30, 2015

I agree it's not ready for stabilization. Specifically the design of the trait over ranges, and then we should impl drain for more collections to see how the API holds up.

@glaebhoerl
Copy link
Contributor

RangeArgument trait brainstorming:

trait IsRange {
    type Idx;
    fn as_range(&self)         -> Range<Option<&Idx>>;
    fn as_mut_range(&mut self) -> Range<Option<&mut Idx>>;
    fn into_range(self)        -> Range<Option<Idx>>;
}
  • I feel like an associated type is more appropriate here? The existing range types all have an index type that's uniquely determined by the type of the range.
  • Instead of a tuple or separate getters, we can use Range itself. I find this appealing aesthetically, though I'm not sure about the ergonomics. This could potentially have positive interactions if we were to give the Range types fleshed-out APIs with inherent impls (e.g. range intersection, smallest-containing-range, ...), as befits their status as built-in types.
  • I'm not sure if there's a way to abstract the three very similar methods into just one, or if not, whether the single trait should perhaps be decomposed into a hierarchy (a la the other Foo, FooMut traits). (If the only implementors are going to be the built-in range types, perhaps not.)
  • I don't like the name RangeArgument. Passing any range type as an argument is a particular use case, but not what the trait is. I'd most like to call it simply Range, but the conflict with struct Range would probably be too annoying. (I don't really like IsRange either - it's just a bad compromise after I couldn't think of anything better.)
  • Can we just use convert::{As, AsMut, Into} instead? (Maybe if we had trait aliases to make it ergonomic...?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants