Skip to content

Commit 1ac8e88

Browse files
committed
auto merge of #8884 : blake2-ppc/rust/exact-size-hint, r=huonw
The message of the first commit explains (edited for changed trait name): The trait `ExactSize` is introduced to solve a few small niggles: * We can't reverse (`.invert()`) an enumeration iterator * for a vector, we have `v.iter().position(f)` but `v.rposition(f)`. * We can't reverse `Zip` even if both iterators are from vectors `ExactSize` is an empty trait that is intended to indicate that an iterator, for example `VecIterator`, knows its exact finite size and reports it correctly using `.size_hint()`. Only adaptors that preserve this at all times, can expose this trait further. (Where here we say finite for fitting in uint). --- It may seem complicated just to solve these small "niggles", (It's really the reversible enumerate case that's the most interesting) but only a few core iterators need to implement this trait. While we gain more capabilities generically for some iterators, it becomes a tad more complicated to figure out if a type has the right trait impls for it.
2 parents 7048e05 + 7c369ee commit 1ac8e88

File tree

10 files changed

+160
-63
lines changed

10 files changed

+160
-63
lines changed

src/libextra/bitv.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,8 @@ impl<'self> DoubleEndedIterator<bool> for BitvIterator<'self> {
608608
}
609609
}
610610

611+
impl<'self> ExactSize<bool> for BitvIterator<'self> {}
612+
611613
impl<'self> RandomAccessIterator<bool> for BitvIterator<'self> {
612614
#[inline]
613615
fn indexable(&self) -> uint {

src/libextra/dlist.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ impl<'self, A> DoubleEndedIterator<&'self A> for DListIterator<'self, A> {
472472
}
473473
}
474474

475+
impl<'self, A> ExactSize<&'self A> for DListIterator<'self, A> {}
476+
475477
impl<'self, A> Iterator<&'self mut A> for MutDListIterator<'self, A> {
476478
#[inline]
477479
fn next(&mut self) -> Option<&'self mut A> {
@@ -508,6 +510,7 @@ impl<'self, A> DoubleEndedIterator<&'self mut A> for MutDListIterator<'self, A>
508510
}
509511
}
510512

513+
impl<'self, A> ExactSize<&'self mut A> for MutDListIterator<'self, A> {}
511514

512515
/// Allow mutating the DList while iterating
513516
pub trait ListInsertion<A> {

src/libextra/num/bigint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ impl BigUint {
525525
#[inline]
526526
pub fn new(v: ~[BigDigit]) -> BigUint {
527527
// omit trailing zeros
528-
let new_len = v.rposition(|n| *n != 0).map_move_default(0, |p| p + 1);
528+
let new_len = v.iter().rposition(|n| *n != 0).map_move_default(0, |p| p + 1);
529529

530530
if new_len == v.len() { return BigUint { data: v }; }
531531
let mut v = v;

src/libextra/ringbuf.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ pub struct RingBufIterator<'self, T> {
243243
iterator!{impl RingBufIterator -> &'self T, get_ref}
244244
iterator_rev!{impl RingBufIterator -> &'self T, get_ref}
245245

246+
impl<'self, T> ExactSize<&'self T> for RingBufIterator<'self, T> {}
247+
246248
impl<'self, T> RandomAccessIterator<&'self T> for RingBufIterator<'self, T> {
247249
#[inline]
248250
fn indexable(&self) -> uint { self.rindex - self.index }
@@ -268,6 +270,8 @@ pub struct RingBufMutIterator<'self, T> {
268270
iterator!{impl RingBufMutIterator -> &'self mut T, get_mut_ref}
269271
iterator_rev!{impl RingBufMutIterator -> &'self mut T, get_mut_ref}
270272

273+
impl<'self, T> ExactSize<&'self mut T> for RingBufMutIterator<'self, T> {}
274+
271275
/// Grow is only called on full elts, so nelts is also len(elts), unlike
272276
/// elsewhere.
273277
fn grow<T>(nelts: uint, loptr: &mut uint, elts: &mut ~[Option<T>]) {

src/libstd/iterator.rs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ implementing the `Iterator` trait.
1818
*/
1919

2020
use cmp;
21-
use num::{Zero, One, Integer, CheckedAdd, Saturating};
21+
use num::{Zero, One, Integer, CheckedAdd, CheckedSub, Saturating};
2222
use option::{Option, Some, None};
2323
use ops::{Add, Mul, Sub};
2424
use cmp::Ord;
@@ -641,6 +641,7 @@ impl<'self, A, T: DoubleEndedIterator<&'self mut A>> MutableDoubleEndedIterator
641641
}
642642
}
643643

644+
644645
/// An object implementing random access indexing by `uint`
645646
///
646647
/// A `RandomAccessIterator` should be either infinite or a `DoubleEndedIterator`.
@@ -653,6 +654,48 @@ pub trait RandomAccessIterator<A>: Iterator<A> {
653654
fn idx(&self, index: uint) -> Option<A>;
654655
}
655656

657+
/// An iterator that knows its exact length
658+
///
659+
/// This trait is a helper for iterators like the vector iterator, so that
660+
/// it can support double-ended enumeration.
661+
///
662+
/// `Iterator::size_hint` *must* return the exact size of the iterator.
663+
/// Note that the size must fit in `uint`.
664+
pub trait ExactSize<A> : DoubleEndedIterator<A> {
665+
/// Return the index of the last element satisfying the specified predicate
666+
///
667+
/// If no element matches, None is returned.
668+
#[inline]
669+
fn rposition(&mut self, predicate: &fn(A) -> bool) -> Option<uint> {
670+
let (lower, upper) = self.size_hint();
671+
assert!(upper == Some(lower));
672+
let mut i = lower;
673+
loop {
674+
match self.next_back() {
675+
None => break,
676+
Some(x) => {
677+
i = match i.checked_sub(&1) {
678+
Some(x) => x,
679+
None => fail!("rposition: incorrect ExactSize")
680+
};
681+
if predicate(x) {
682+
return Some(i)
683+
}
684+
}
685+
}
686+
}
687+
None
688+
}
689+
}
690+
691+
// All adaptors that preserve the size of the wrapped iterator are fine
692+
// Adaptors that may overflow in `size_hint` are not, i.e. `Chain`.
693+
impl<A, T: ExactSize<A>> ExactSize<(uint, A)> for Enumerate<T> {}
694+
impl<'self, A, T: ExactSize<A>> ExactSize<A> for Inspect<'self, A, T> {}
695+
impl<A, T: ExactSize<A>> ExactSize<A> for Invert<T> {}
696+
impl<'self, A, B, T: ExactSize<A>> ExactSize<B> for Map<'self, A, B, T> {}
697+
impl<A, B, T: ExactSize<A>, U: ExactSize<B>> ExactSize<(A, B)> for Zip<T, U> {}
698+
656699
/// An double-ended iterator with the direction inverted
657700
#[deriving(Clone)]
658701
pub struct Invert<T> {
@@ -956,6 +999,29 @@ impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<(A, B)> for Zip<T, U> {
956999
}
9571000
}
9581001

1002+
impl<A, B, T: ExactSize<A>, U: ExactSize<B>> DoubleEndedIterator<(A, B)>
1003+
for Zip<T, U> {
1004+
#[inline]
1005+
fn next_back(&mut self) -> Option<(A, B)> {
1006+
let (a_sz, a_upper) = self.a.size_hint();
1007+
let (b_sz, b_upper) = self.b.size_hint();
1008+
assert!(a_upper == Some(a_sz));
1009+
assert!(b_upper == Some(b_sz));
1010+
if a_sz < b_sz {
1011+
for _ in range(0, b_sz - a_sz) { self.b.next_back(); }
1012+
} else if a_sz > b_sz {
1013+
for _ in range(0, a_sz - b_sz) { self.a.next_back(); }
1014+
}
1015+
let (a_sz, _) = self.a.size_hint();
1016+
let (b_sz, _) = self.b.size_hint();
1017+
assert!(a_sz == b_sz);
1018+
match (self.a.next_back(), self.b.next_back()) {
1019+
(Some(x), Some(y)) => Some((x, y)),
1020+
_ => None
1021+
}
1022+
}
1023+
}
1024+
9591025
impl<A, B, T: RandomAccessIterator<A>, U: RandomAccessIterator<B>>
9601026
RandomAccessIterator<(A, B)> for Zip<T, U> {
9611027
#[inline]
@@ -1137,6 +1203,20 @@ impl<A, T: Iterator<A>> Iterator<(uint, A)> for Enumerate<T> {
11371203
}
11381204
}
11391205

1206+
impl<A, T: ExactSize<A>> DoubleEndedIterator<(uint, A)> for Enumerate<T> {
1207+
#[inline]
1208+
fn next_back(&mut self) -> Option<(uint, A)> {
1209+
match self.iter.next_back() {
1210+
Some(a) => {
1211+
let (lower, upper) = self.iter.size_hint();
1212+
assert!(upper == Some(lower));
1213+
Some((self.count + lower, a))
1214+
}
1215+
_ => None
1216+
}
1217+
}
1218+
}
1219+
11401220
impl<A, T: RandomAccessIterator<A>> RandomAccessIterator<(uint, A)> for Enumerate<T> {
11411221
#[inline]
11421222
fn indexable(&self) -> uint {
@@ -2331,6 +2411,33 @@ mod tests {
23312411
assert_eq!(it.next(), None);
23322412
}
23332413

2414+
#[test]
2415+
fn test_double_ended_enumerate() {
2416+
let xs = [1, 2, 3, 4, 5, 6];
2417+
let mut it = xs.iter().map(|&x| x).enumerate();
2418+
assert_eq!(it.next(), Some((0, 1)));
2419+
assert_eq!(it.next(), Some((1, 2)));
2420+
assert_eq!(it.next_back(), Some((5, 6)));
2421+
assert_eq!(it.next_back(), Some((4, 5)));
2422+
assert_eq!(it.next_back(), Some((3, 4)));
2423+
assert_eq!(it.next_back(), Some((2, 3)));
2424+
assert_eq!(it.next(), None);
2425+
}
2426+
2427+
#[test]
2428+
fn test_double_ended_zip() {
2429+
let xs = [1, 2, 3, 4, 5, 6];
2430+
let ys = [1, 2, 3, 7];
2431+
let a = xs.iter().map(|&x| x);
2432+
let b = ys.iter().map(|&x| x);
2433+
let mut it = a.zip(b);
2434+
assert_eq!(it.next(), Some((1, 1)));
2435+
assert_eq!(it.next(), Some((2, 2)));
2436+
assert_eq!(it.next_back(), Some((4, 7)));
2437+
assert_eq!(it.next_back(), Some((3, 3)));
2438+
assert_eq!(it.next(), None);
2439+
}
2440+
23342441
#[test]
23352442
fn test_double_ended_filter() {
23362443
let xs = [1, 2, 3, 4, 5, 6];
@@ -2367,6 +2474,31 @@ mod tests {
23672474
assert_eq!(it.next_back(), None)
23682475
}
23692476

2477+
#[test]
2478+
fn test_rposition() {
2479+
fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' }
2480+
fn g(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'd' }
2481+
let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')];
2482+
2483+
assert_eq!(v.iter().rposition(f), Some(3u));
2484+
assert!(v.iter().rposition(g).is_none());
2485+
}
2486+
2487+
#[test]
2488+
#[should_fail]
2489+
fn test_rposition_fail() {
2490+
let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)];
2491+
let mut i = 0;
2492+
do v.iter().rposition |_elt| {
2493+
if i == 2 {
2494+
fail!()
2495+
}
2496+
i += 1;
2497+
false
2498+
};
2499+
}
2500+
2501+
23702502
#[cfg(test)]
23712503
fn check_randacc_iter<A: Eq, T: Clone + RandomAccessIterator<A>>(a: T, len: uint)
23722504
{

src/libstd/option.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use cmp::{Eq,Ord};
4646
use util;
4747
use num::Zero;
4848
use iterator;
49-
use iterator::{Iterator, DoubleEndedIterator};
49+
use iterator::{Iterator, DoubleEndedIterator, ExactSize};
5050
use str::{StrSlice, OwnedStr};
5151
use to_str::ToStr;
5252
use clone::DeepClone;
@@ -402,6 +402,8 @@ impl<A> DoubleEndedIterator<A> for OptionIterator<A> {
402402
}
403403
}
404404

405+
impl<A> ExactSize<A> for OptionIterator<A> {}
406+
405407
#[cfg(test)]
406408
mod tests {
407409
use super::*;

src/libstd/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub use hash::Hash;
5252
pub use num::Times;
5353
pub use iterator::{FromIterator, Extendable};
5454
pub use iterator::{Iterator, DoubleEndedIterator, RandomAccessIterator, ClonableIterator};
55-
pub use iterator::{OrdIterator, MutableDoubleEndedIterator};
55+
pub use iterator::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
5656
pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
5757
pub use num::{Orderable, Signed, Unsigned, Round};
5858
pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic};

src/libstd/select.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use cell::Cell;
1212
use comm;
1313
use container::Container;
14-
use iterator::Iterator;
14+
use iterator::{Iterator, DoubleEndedIterator};
1515
use option::*;
1616
// use either::{Either, Left, Right};
1717
// use rt::kill::BlockedTask;
@@ -87,7 +87,7 @@ pub fn select<A: Select>(ports: &mut [A]) -> uint {
8787
// Task resumes. Now unblock ourselves from all the ports we blocked on.
8888
// If the success index wasn't reset, 'take' will just take all of them.
8989
// Iterate in reverse so the 'earliest' index that's ready gets returned.
90-
for (index, port) in ports.mut_slice(0, ready_index).mut_rev_iter().enumerate() {
90+
for (index, port) in ports.mut_slice(0, ready_index).mut_iter().enumerate().invert() {
9191
if port.unblock_from() {
9292
ready_index = index;
9393
}

src/libstd/str.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use container::{Container, Mutable};
2424
use num::Times;
2525
use iterator::{Iterator, FromIterator, Extendable};
2626
use iterator::{Filter, AdditiveIterator, Map};
27-
use iterator::{Invert, DoubleEndedIterator};
27+
use iterator::{Invert, DoubleEndedIterator, ExactSize};
2828
use libc;
2929
use num::{Saturating};
3030
use option::{None, Option, Some};
@@ -484,9 +484,8 @@ for CharSplitIterator<'self, Sep> {
484484
let mut next_split = None;
485485

486486
if self.only_ascii {
487-
for (j, byte) in self.string.byte_rev_iter().enumerate() {
487+
for (idx, byte) in self.string.byte_iter().enumerate().invert() {
488488
if self.sep.matches(byte as char) && byte < 128u8 {
489-
let idx = len - j - 1;
490489
next_split = Some((idx, idx + 1));
491490
break;
492491
}
@@ -2006,16 +2005,13 @@ impl<'self> StrSlice<'self> for &'self str {
20062005
/// or `None` if there is no match
20072006
fn find<C: CharEq>(&self, search: C) -> Option<uint> {
20082007
if search.only_ascii() {
2009-
for (i, b) in self.byte_iter().enumerate() {
2010-
if search.matches(b as char) { return Some(i) }
2011-
}
2008+
self.byte_iter().position(|b| search.matches(b as char))
20122009
} else {
20132010
for (index, c) in self.char_offset_iter() {
20142011
if search.matches(c) { return Some(index); }
20152012
}
2013+
None
20162014
}
2017-
2018-
None
20192015
}
20202016
20212017
/// Returns the byte index of the last character of `self` that matches `search`
@@ -2026,18 +2022,13 @@ impl<'self> StrSlice<'self> for &'self str {
20262022
/// or `None` if there is no match
20272023
fn rfind<C: CharEq>(&self, search: C) -> Option<uint> {
20282024
if search.only_ascii() {
2029-
let mut index = self.len();
2030-
for b in self.byte_rev_iter() {
2031-
index -= 1;
2032-
if search.matches(b as char) { return Some(index); }
2033-
}
2025+
self.byte_iter().rposition(|b| search.matches(b as char))
20342026
} else {
20352027
for (index, c) in self.char_offset_rev_iter() {
20362028
if search.matches(c) { return Some(index); }
20372029
}
2030+
None
20382031
}
2039-
2040-
None
20412032
}
20422033
20432034
/// Returns the byte index of the first matching substring

0 commit comments

Comments
 (0)