diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index 85e1e133b7d..782a5e31d2d 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -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 BTreeMap { 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, Bound)`, 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 BTreeMap { /// #![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(&self, - min: Bound<&Min>, - max: Bound<&Max>) - -> Range - where K: Borrow + Borrow + pub fn range(&self, range: R) -> Range + where T: Ord, K: Borrow, R: RangeArgument { + 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 BTreeMap { } } - /// 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, Bound)`, 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 BTreeMap { #[unstable(feature = "btree_range", reason = "matches collection reform specification, waiting for dust to settle", issue = "27787")] - pub fn range_mut(&mut self, - min: Bound<&Min>, - max: Bound<&Max>) - -> RangeMut - where K: Borrow + Borrow + pub fn range_mut(&mut self, range: R) -> RangeMut + where T: Ord, K: Borrow, R: RangeArgument { + let min = range.start(); + let max = range.end(); let root1 = self.root.as_mut(); let root2 = unsafe { ptr::read(&root1) }; diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index 34674e3a0bd..f472b593b67 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -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 BTreeSet { } impl BTreeSet { - /// 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, Bound)`, 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 BTreeSet { /// #![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 + Borrow + pub fn range(&self, range: R) -> Range + where K: Ord, T: Borrow, R: RangeArgument { - Range { iter: self.map.range(min, max) } + Range { iter: self.map.range(range) } } } diff --git a/src/libcollections/range.rs b/src/libcollections/range.rs index d331ead2c5e..1df4ace3777 100644 --- a/src/libcollections/range.rs +++ b/src/libcollections/range.rs @@ -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 { - /// Start index (inclusive) +pub trait RangeArgument { + /// 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 RangeArgument for RangeFull {} +impl RangeArgument for RangeFull { + fn start(&self) -> Bound<&T> { + Unbounded + } + fn end(&self) -> Bound<&T> { + Unbounded + } +} impl RangeArgument for RangeFrom { - fn start(&self) -> Option<&T> { - Some(&self.start) + fn start(&self) -> Bound<&T> { + Included(&self.start) + } + fn end(&self) -> Bound<&T> { + Unbounded } } impl RangeArgument for RangeTo { - fn end(&self) -> Option<&T> { - Some(&self.end) + fn start(&self) -> Bound<&T> { + Unbounded + } + fn end(&self) -> Bound<&T> { + Excluded(&self.end) } } impl RangeArgument for Range { - 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 RangeArgument for (Bound, Bound) { + 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 for (Bound<&'a T>, Bound<&'a T>) { + fn start(&self) -> Bound<&T> { + self.0 + } + + fn end(&self) -> Bound<&T> { + self.1 } } diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 157c762b4a7..5210c25b4e5 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -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. diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 27cf0268c99..4b05f8062e8 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -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` but pronounced 'vector'. /// @@ -1060,8 +1061,16 @@ impl Vec { // 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); diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 76e44c81579..fea2d111f47 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -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 VecDeque { // 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"); diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs index 8222da105cc..c84753415a2 100644 --- a/src/libcollectionstest/btree/map.rs +++ b/src/libcollectionstest/btree/map.rs @@ -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, 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::((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()) { diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 844e9041d20..c0b5b7f5173 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -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 ArrayVec { // 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); diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 4623e52ffc2..b003cba292c 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -27,6 +27,7 @@ #![feature(shared)] #![feature(collections_range)] +#![feature(collections_bound)] #![feature(nonzero)] #![feature(rustc_private)] #![feature(staged_api)] diff --git a/src/test/run-pass/sync-send-iterators-in-libcollections.rs b/src/test/run-pass/sync-send-iterators-in-libcollections.rs index 45ac334dc1d..d046705c94b 100644 --- a/src/test/run-pass/sync-send-iterators-in-libcollections.rs +++ b/src/test/run-pass/sync-send-iterators-in-libcollections.rs @@ -57,11 +57,11 @@ fn main() { all_sync_send!(BinaryHeap::::new(), iter, drain, into_iter); all_sync_send!(BTreeMap::::new(), iter, iter_mut, into_iter, keys, values); - is_sync_send!(BTreeMap::::new(), range(Included(&0), Included(&9))); - is_sync_send!(BTreeMap::::new(), range_mut(Included(&0), Included(&9))); + is_sync_send!(BTreeMap::::new(), range((Included(&0), Included(&9)))); + is_sync_send!(BTreeMap::::new(), range_mut((Included(&0), Included(&9)))); all_sync_send!(BTreeSet::::new(), iter, into_iter); - is_sync_send!(BTreeSet::::new(), range(Included(&0), Included(&9))); + is_sync_send!(BTreeSet::::new(), range((Included(&0), Included(&9)))); is_sync_send!(BTreeSet::::new(), difference(&BTreeSet::::new())); is_sync_send!(BTreeSet::::new(), symmetric_difference(&BTreeSet::::new())); is_sync_send!(BTreeSet::::new(), intersection(&BTreeSet::::new()));