Skip to content

Commit

Permalink
Make OrdMap::range not implement ExactSizedIterator (#85)
Browse files Browse the repository at this point in the history
Make OrdMap::range return a wrapper that doesn't implement ExactSizedIterator

Fixes #84
  • Loading branch information
jneem authored Dec 16, 2024
1 parent 38699a7 commit aa97553
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 11 deletions.
3 changes: 1 addition & 2 deletions src/nodes/btree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,8 +1192,7 @@ impl<'a, A: 'a + BTreeValue> Iterator for Iter<'a, A> {
}

fn size_hint(&self) -> (usize, Option<usize>) {
// (0, Some(self.remaining))
(0, None)
(0, Some(self.remaining))
}
}

Expand Down
61 changes: 52 additions & 9 deletions src/ord/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ use std::mem;
use std::ops::{Add, Index, IndexMut, RangeBounds};

use crate::hashmap::HashMap;
use crate::nodes::btree::{BTreeValue, Insert, Node, Remove};
use crate::nodes::btree::{BTreeValue, Insert, Iter as NodeIter, Node, Remove};
#[cfg(has_specialisation)]

Check warning on line 31 in src/ord/map.rs

View workflow job for this annotation

GitHub Actions / clippy

unexpected `cfg` condition name: `has_specialisation`

warning: unexpected `cfg` condition name: `has_specialisation` --> src/ord/map.rs:31:7 | 31 | #[cfg(has_specialisation)] | ^^^^^^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_specialisation)'] } = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(has_specialisation)");` to the top of the `build.rs` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
use crate::util::linear_search_by;
use crate::util::{Pool, PoolRef};

pub use crate::nodes::btree::{
ConsumingIter, DiffItem as NodeDiffItem, DiffIter as NodeDiffIter, Iter as RangedIter,
};
pub use crate::nodes::btree::{ConsumingIter, DiffItem as NodeDiffItem, DiffIter as NodeDiffIter};

/// Construct a map from a sequence of key/value pairs.
///
Expand Down Expand Up @@ -358,20 +356,20 @@ where
#[must_use]
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
it: RangedIter::new(&self.root, self.size, ..),
it: NodeIter::new(&self.root, self.size, ..),
}
}

/// Create an iterator over a range of key/value pairs.
#[must_use]
pub fn range<R, BK>(&self, range: R) -> Iter<'_, K, V>
pub fn range<R, BK>(&self, range: R) -> RangedIter<'_, K, V>
where
R: RangeBounds<BK>,
K: Borrow<BK>,
BK: Ord + ?Sized,
{
Iter {
it: RangedIter::new(&self.root, self.size, range),
RangedIter {
it: NodeIter::new(&self.root, self.size, range),
}
}

Expand Down Expand Up @@ -1868,7 +1866,7 @@ where

/// An iterator over the key/value pairs of a map.
pub struct Iter<'a, K, V> {
it: RangedIter<'a, (K, V)>,
it: NodeIter<'a, (K, V)>,
}

// We impl Clone instead of deriving it, because we want Clone even if K and V aren't.
Expand All @@ -1890,6 +1888,8 @@ where
self.it.next().map(|(k, v)| (k, v))
}

// We only construct an `Iter` when the range is full, meaning that we can
// override `size_hint` and implement `ExactSizeIterator`.
fn size_hint(&self) -> (usize, Option<usize>) {
(self.it.remaining, Some(self.it.remaining))
}
Expand All @@ -1906,6 +1906,44 @@ where

impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> where (K, V): 'a + BTreeValue {}

/// An iterator over a range of key/value pairs in a map.
pub struct RangedIter<'a, K, V> {
it: NodeIter<'a, (K, V)>,
}

// We impl Clone instead of deriving it, because we want Clone even if K and V aren't.
impl<'a, K, V> Clone for RangedIter<'a, K, V> {

Check warning on line 1915 in src/ord/map.rs

View workflow job for this annotation

GitHub Actions / clippy

the following explicit lifetimes could be elided: 'a

warning: the following explicit lifetimes could be elided: 'a --> src/ord/map.rs:1915:6 | 1915 | impl<'a, K, V> Clone for RangedIter<'a, K, V> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1915 - impl<'a, K, V> Clone for RangedIter<'a, K, V> { 1915 + impl<K, V> Clone for RangedIter<'_, K, V> { |
fn clone(&self) -> Self {
RangedIter {
it: self.it.clone(),
}
}
}

impl<'a, K, V> Iterator for RangedIter<'a, K, V>
where
(K, V): 'a + BTreeValue,
{
type Item = (&'a K, &'a V);

fn next(&mut self) -> Option<Self::Item> {
self.it.next().map(|(k, v)| (k, v))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.it.size_hint()
}
}

impl<'a, K, V> DoubleEndedIterator for RangedIter<'a, K, V>
where
(K, V): 'a + BTreeValue,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.it.next_back().map(|(k, v)| (k, v))
}
}

/// An iterator over the differences between two maps.
pub struct DiffIter<'a, 'b, K, V> {
it: NodeDiffIter<'a, 'b, (K, V)>,
Expand Down Expand Up @@ -2431,6 +2469,11 @@ mod test {
assert_eq!(vec![(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], range);
let range: Vec<(i32, i32)> = map.range(..=6).map(|(k, v)| (*k, *v)).collect();
assert_eq!(vec![(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], range);

assert_eq!(map.range(2..5).size_hint(), (0, Some(6)));
let mut iter = map.range(2..5);
iter.next();
assert_eq!(iter.size_hint(), (0, Some(5)));
}

#[test]
Expand Down

0 comments on commit aa97553

Please sign in to comment.