Auto merge of #38610 - djzin:master, r=sfackler

Implementation of plan in issue #27787 for btree_range

Still some ergonomics to be worked on, the ::<str,_> is particularly unsightly
This commit is contained in:
bors 2017-01-15 21:44:51 +00:00
commit 0ef85a6b1c
10 changed files with 190 additions and 77 deletions

View file

@ -17,7 +17,8 @@ use core::ops::Index;
use core::{fmt, intrinsics, mem, ptr};
use borrow::Borrow;
use Bound::{self, Excluded, Included, Unbounded};
use Bound::{Excluded, Included, Unbounded};
use range::RangeArgument;
use super::node::{self, Handle, NodeRef, marker};
use super::search;
@ -654,10 +655,12 @@ impl<K: Ord, V> BTreeMap<K, V> {
self.fix_right_edge();
}
/// Constructs a double-ended iterator over a sub-range of elements in the map, starting
/// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
/// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
/// Thus range(Unbounded, Unbounded) will yield the whole collection.
/// Constructs a double-ended iterator over a sub-range of elements in the map.
/// The simplest way is to use the range synax `min..max`, thus `range(min..max)` will
/// yield elements from min (inclusive) to max (exclusive).
/// The range may also be entered as `(Bound<T>, Bound<T>)`, so for example
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
/// range from 4 to 10.
///
/// # Examples
///
@ -667,26 +670,25 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// #![feature(btree_range, collections_bound)]
///
/// use std::collections::BTreeMap;
/// use std::collections::Bound::{Included, Unbounded};
/// use std::collections::Bound::Included;
///
/// let mut map = BTreeMap::new();
/// map.insert(3, "a");
/// map.insert(5, "b");
/// map.insert(8, "c");
/// for (&key, &value) in map.range(Included(&4), Included(&8)) {
/// for (&key, &value) in map.range((Included(&4), Included(&8))) {
/// println!("{}: {}", key, value);
/// }
/// assert_eq!(Some((&5, &"b")), map.range(Included(&4), Unbounded).next());
/// assert_eq!(Some((&5, &"b")), map.range(4..).next());
/// ```
#[unstable(feature = "btree_range",
reason = "matches collection reform specification, waiting for dust to settle",
issue = "27787")]
pub fn range<Min: ?Sized + Ord, Max: ?Sized + Ord>(&self,
min: Bound<&Min>,
max: Bound<&Max>)
-> Range<K, V>
where K: Borrow<Min> + Borrow<Max>
pub fn range<T: ?Sized, R>(&self, range: R) -> Range<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
{
let min = range.start();
let max = range.end();
let front = match min {
Included(key) => {
match search::search_tree(self.root.as_ref(), key) {
@ -745,25 +747,26 @@ impl<K: Ord, V> BTreeMap<K, V> {
}
}
/// Constructs a mutable double-ended iterator over a sub-range of elements in the map, starting
/// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
/// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
/// Thus range(Unbounded, Unbounded) will yield the whole collection.
/// Constructs a mutable double-ended iterator over a sub-range of elements in the map.
/// The simplest way is to use the range synax `min..max`, thus `range(min..max)` will
/// yield elements from min (inclusive) to max (exclusive).
/// The range may also be entered as `(Bound<T>, Bound<T>)`, so for example
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
/// range from 4 to 10.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(btree_range, collections_bound)]
/// #![feature(btree_range)]
///
/// use std::collections::BTreeMap;
/// use std::collections::Bound::{Included, Excluded};
///
/// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter()
/// .map(|&s| (s, 0))
/// .collect();
/// for (_, balance) in map.range_mut(Included("B"), Excluded("Cheryl")) {
/// for (_, balance) in map.range_mut("B".."Cheryl") {
/// *balance += 100;
/// }
/// for (name, balance) in &map {
@ -773,12 +776,11 @@ impl<K: Ord, V> BTreeMap<K, V> {
#[unstable(feature = "btree_range",
reason = "matches collection reform specification, waiting for dust to settle",
issue = "27787")]
pub fn range_mut<Min: ?Sized + Ord, Max: ?Sized + Ord>(&mut self,
min: Bound<&Min>,
max: Bound<&Max>)
-> RangeMut<K, V>
where K: Borrow<Min> + Borrow<Max>
pub fn range_mut<T: ?Sized, R>(&mut self, range: R) -> RangeMut<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
{
let min = range.start();
let max = range.end();
let root1 = self.root.as_mut();
let root2 = unsafe { ptr::read(&root1) };

View file

@ -21,7 +21,7 @@ use core::ops::{BitOr, BitAnd, BitXor, Sub};
use borrow::Borrow;
use btree_map::{BTreeMap, Keys};
use super::Recover;
use Bound;
use range::RangeArgument;
// FIXME(conventions): implement bounded iterators
@ -207,10 +207,12 @@ impl<T> BTreeSet<T> {
}
impl<T: Ord> BTreeSet<T> {
/// Constructs a double-ended iterator over a sub-range of elements in the set, starting
/// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
/// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
/// Thus range(Unbounded, Unbounded) will yield the whole collection.
/// Constructs a double-ended iterator over a sub-range of elements in the set.
/// The simplest way is to use the range synax `min..max`, thus `range(min..max)` will
/// yield elements from min (inclusive) to max (exclusive).
/// The range may also be entered as `(Bound<T>, Bound<T>)`, so for example
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
/// range from 4 to 10.
///
/// # Examples
///
@ -218,27 +220,24 @@ impl<T: Ord> BTreeSet<T> {
/// #![feature(btree_range, collections_bound)]
///
/// use std::collections::BTreeSet;
/// use std::collections::Bound::{Included, Unbounded};
/// use std::collections::Bound::Included;
///
/// let mut set = BTreeSet::new();
/// set.insert(3);
/// set.insert(5);
/// set.insert(8);
/// for &elem in set.range(Included(&4), Included(&8)) {
/// for &elem in set.range((Included(&4), Included(&8))) {
/// println!("{}", elem);
/// }
/// assert_eq!(Some(&5), set.range(Included(&4), Unbounded).next());
/// assert_eq!(Some(&5), set.range(4..).next());
/// ```
#[unstable(feature = "btree_range",
reason = "matches collection reform specification, waiting for dust to settle",
issue = "27787")]
pub fn range<'a, Min: ?Sized + Ord, Max: ?Sized + Ord>(&'a self,
min: Bound<&Min>,
max: Bound<&Max>)
-> Range<'a, T>
where T: Borrow<Min> + Borrow<Max>
pub fn range<K: ?Sized, R>(&self, range: R) -> Range<T>
where K: Ord, T: Borrow<K>, R: RangeArgument<K>
{
Range { iter: self.map.range(min, max) }
Range { iter: self.map.range(range) }
}
}

View file

@ -15,78 +15,120 @@
//! Range syntax.
use core::ops::{RangeFull, Range, RangeTo, RangeFrom};
use Bound::{self, Excluded, Included, Unbounded};
/// **RangeArgument** is implemented by Rust's built-in range types, produced
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
pub trait RangeArgument<T> {
/// Start index (inclusive)
pub trait RangeArgument<T: ?Sized> {
/// Start index bound
///
/// Return start value if present, else `None`.
/// Return start value as a `Bound`
///
/// # Examples
///
/// ```
/// #![feature(collections)]
/// #![feature(collections_range)]
/// #![feature(collections_bound)]
///
/// extern crate collections;
///
/// # fn main() {
/// use collections::range::RangeArgument;
/// use collections::Bound::*;
///
/// assert_eq!((..10).start(), None);
/// assert_eq!((3..10).start(), Some(&3));
/// assert_eq!((..10).start(), Unbounded);
/// assert_eq!((3..10).start(), Included(&3));
/// # }
/// ```
fn start(&self) -> Option<&T> {
None
}
fn start(&self) -> Bound<&T>;
/// End index (exclusive)
/// End index bound
///
/// Return end value if present, else `None`.
/// Return end value as a `Bound`
///
/// # Examples
///
/// ```
/// #![feature(collections)]
/// #![feature(collections_range)]
/// #![feature(collections_bound)]
///
/// extern crate collections;
///
/// # fn main() {
/// use collections::range::RangeArgument;
/// use collections::Bound::*;
///
/// assert_eq!((3..).end(), None);
/// assert_eq!((3..10).end(), Some(&10));
/// assert_eq!((3..).end(), Unbounded);
/// assert_eq!((3..10).end(), Excluded(&10));
/// # }
/// ```
fn end(&self) -> Option<&T> {
None
}
fn end(&self) -> Bound<&T>;
}
// FIXME add inclusive ranges to RangeArgument
impl<T> RangeArgument<T> for RangeFull {}
impl<T: ?Sized> RangeArgument<T> for RangeFull {
fn start(&self) -> Bound<&T> {
Unbounded
}
fn end(&self) -> Bound<&T> {
Unbounded
}
}
impl<T> RangeArgument<T> for RangeFrom<T> {
fn start(&self) -> Option<&T> {
Some(&self.start)
fn start(&self) -> Bound<&T> {
Included(&self.start)
}
fn end(&self) -> Bound<&T> {
Unbounded
}
}
impl<T> RangeArgument<T> for RangeTo<T> {
fn end(&self) -> Option<&T> {
Some(&self.end)
fn start(&self) -> Bound<&T> {
Unbounded
}
fn end(&self) -> Bound<&T> {
Excluded(&self.end)
}
}
impl<T> RangeArgument<T> for Range<T> {
fn start(&self) -> Option<&T> {
Some(&self.start)
fn start(&self) -> Bound<&T> {
Included(&self.start)
}
fn end(&self) -> Option<&T> {
Some(&self.end)
fn end(&self) -> Bound<&T> {
Excluded(&self.end)
}
}
impl<T> RangeArgument<T> for (Bound<T>, Bound<T>) {
fn start(&self) -> Bound<&T> {
match *self {
(Included(ref start), _) => Included(start),
(Excluded(ref start), _) => Excluded(start),
(Unbounded, _) => Unbounded,
}
}
fn end(&self) -> Bound<&T> {
match *self {
(_, Included(ref end)) => Included(end),
(_, Excluded(ref end)) => Excluded(end),
(_, Unbounded) => Unbounded,
}
}
}
impl<'a, T: ?Sized + 'a> RangeArgument<T> for (Bound<&'a T>, Bound<&'a T>) {
fn start(&self) -> Bound<&T> {
self.0
}
fn end(&self) -> Bound<&T> {
self.1
}
}

View file

@ -68,6 +68,7 @@ use std_unicode::str as unicode_str;
use borrow::{Cow, ToOwned};
use range::RangeArgument;
use Bound::{Excluded, Included, Unbounded};
use str::{self, FromStr, Utf8Error, Chars};
use vec::Vec;
use boxed::Box;
@ -1350,8 +1351,16 @@ impl String {
// Because the range removal happens in Drop, if the Drain iterator is leaked,
// the removal will not happen.
let len = self.len();
let start = *range.start().unwrap_or(&0);
let end = *range.end().unwrap_or(&len);
let start = match range.start() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
// Take out two simultaneous borrows. The &mut String won't be accessed
// until iteration is over, in Drop.

View file

@ -84,6 +84,7 @@ use core::ptr::Shared;
use core::slice;
use super::range::RangeArgument;
use Bound::{Excluded, Included, Unbounded};
/// A contiguous growable array type, written `Vec<T>` but pronounced 'vector'.
///
@ -1060,8 +1061,16 @@ impl<T> Vec<T> {
// the hole, and the vector length is restored to the new length.
//
let len = self.len();
let start = *range.start().unwrap_or(&0);
let end = *range.end().unwrap_or(&len);
let start = match range.start() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
assert!(start <= end);
assert!(end <= len);

View file

@ -33,6 +33,7 @@ use core::cmp;
use alloc::raw_vec::RawVec;
use super::range::RangeArgument;
use Bound::{Excluded, Included, Unbounded};
use super::vec::Vec;
const INITIAL_CAPACITY: usize = 7; // 2^3 - 1
@ -852,8 +853,16 @@ impl<T> VecDeque<T> {
// and the head/tail values will be restored correctly.
//
let len = self.len();
let start = *range.start().unwrap_or(&0);
let end = *range.end().unwrap_or(&len);
let start = match range.start() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
assert!(start <= end, "drain lower bound was too large");
assert!(end <= len, "drain upper bound was too large");

View file

@ -170,7 +170,7 @@ fn test_range_small() {
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
let mut j = 0;
for ((&k, &v), i) in map.range(Included(&2), Unbounded).zip(2..size) {
for ((&k, &v), i) in map.range(2..).zip(2..size) {
assert_eq!(k, i);
assert_eq!(v, i);
j += 1;
@ -184,7 +184,7 @@ fn test_range_1000() {
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
fn test(map: &BTreeMap<u32, u32>, size: u32, min: Bound<&u32>, max: Bound<&u32>) {
let mut kvs = map.range(min, max).map(|(&k, &v)| (k, v));
let mut kvs = map.range((min, max)).map(|(&k, &v)| (k, v));
let mut pairs = (0..size).map(|i| (i, i));
for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) {
@ -201,6 +201,20 @@ fn test_range_1000() {
test(&map, size, Unbounded, Unbounded);
}
#[test]
fn test_range_borrowed_key() {
let mut map = BTreeMap::new();
map.insert("aardvark".to_string(), 1);
map.insert("baboon".to_string(), 2);
map.insert("coyote".to_string(), 3);
map.insert("dingo".to_string(), 4);
// NOTE: would like to use simply "b".."d" here...
let mut iter = map.range::<str, _>((Included("b"),Excluded("d")));
assert_eq!(iter.next(), Some((&"baboon".to_string(), &2)));
assert_eq!(iter.next(), Some((&"coyote".to_string(), &3)));
assert_eq!(iter.next(), None);
}
#[test]
fn test_range() {
let size = 200;
@ -208,7 +222,26 @@ fn test_range() {
for i in 0..size {
for j in i..size {
let mut kvs = map.range(Included(&i), Included(&j)).map(|(&k, &v)| (k, v));
let mut kvs = map.range((Included(&i), Included(&j))).map(|(&k, &v)| (k, v));
let mut pairs = (i..j + 1).map(|i| (i, i));
for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) {
assert_eq!(kv, pair);
}
assert_eq!(kvs.next(), None);
assert_eq!(pairs.next(), None);
}
}
}
#[test]
fn test_range_mut() {
let size = 200;
let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
for i in 0..size {
for j in i..size {
let mut kvs = map.range_mut((Included(&i), Included(&j))).map(|(&k, &mut v)| (k, v));
let mut pairs = (i..j + 1).map(|i| (i, i));
for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) {

View file

@ -19,6 +19,7 @@ use std::slice;
use std::fmt;
use std::mem;
use std::collections::range::RangeArgument;
use std::collections::Bound::{Excluded, Included, Unbounded};
pub unsafe trait Array {
type Element;
@ -119,8 +120,16 @@ impl<A: Array> ArrayVec<A> {
// the hole, and the vector length is restored to the new length.
//
let len = self.len();
let start = *range.start().unwrap_or(&0);
let end = *range.end().unwrap_or(&len);
let start = match range.start() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
assert!(start <= end);
assert!(end <= len);

View file

@ -27,6 +27,7 @@
#![feature(shared)]
#![feature(collections_range)]
#![feature(collections_bound)]
#![feature(nonzero)]
#![feature(rustc_private)]
#![feature(staged_api)]

View file

@ -57,11 +57,11 @@ fn main() {
all_sync_send!(BinaryHeap::<usize>::new(), iter, drain, into_iter);
all_sync_send!(BTreeMap::<usize, usize>::new(), iter, iter_mut, into_iter, keys, values);
is_sync_send!(BTreeMap::<usize, usize>::new(), range(Included(&0), Included(&9)));
is_sync_send!(BTreeMap::<usize, usize>::new(), range_mut(Included(&0), Included(&9)));
is_sync_send!(BTreeMap::<usize, usize>::new(), range((Included(&0), Included(&9))));
is_sync_send!(BTreeMap::<usize, usize>::new(), range_mut((Included(&0), Included(&9))));
all_sync_send!(BTreeSet::<usize>::new(), iter, into_iter);
is_sync_send!(BTreeSet::<usize>::new(), range(Included(&0), Included(&9)));
is_sync_send!(BTreeSet::<usize>::new(), range((Included(&0), Included(&9))));
is_sync_send!(BTreeSet::<usize>::new(), difference(&BTreeSet::<usize>::new()));
is_sync_send!(BTreeSet::<usize>::new(), symmetric_difference(&BTreeSet::<usize>::new()));
is_sync_send!(BTreeSet::<usize>::new(), intersection(&BTreeSet::<usize>::new()));