From 737800323a1476e4a87ae322f7da6c1a972cd34b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 22 Jul 2020 17:07:08 -0700 Subject: [PATCH 1/2] Implement direct usize indexing ```rust where IndexMap: IndexMut, IndexSet: Index, ``` This allows `map[i]` and `set[i]` indexing to access values directly, panicking if the index is out of bounds, similar to slices. On maps, this somewhat overlaps with `Index<&Q> + IndexMut<&Q>` where `Q: Equivalent`. The reference makes this indexing unambiguous, but it could be confusing to users if the key type is also an integer. --- src/map.rs | 24 ++++++++++++++++++++++++ src/set.rs | 12 +++++++++++- tests/quick.rs | 22 +++++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/map.rs b/src/map.rs index bba4af98..f4ef97c8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1023,6 +1023,30 @@ where } } +impl Index for IndexMap { + type Output = V; + + /// ***Panics*** if `index` is out of bounds. + fn index(&self, index: usize) -> &V { + self.get_index(index) + .expect("IndexMap: index out of bounds") + .1 + } +} + +/// Mutable indexing allows changing / updating indexed values +/// that are already present. +/// +/// You can **not** insert new values with index syntax, use `.insert()`. +impl IndexMut for IndexMap { + /// ***Panics*** if `index` is out of bounds. + fn index_mut(&mut self, index: usize) -> &mut V { + self.get_index_mut(index) + .expect("IndexMap: index out of bounds") + .1 + } +} + impl FromIterator<(K, V)> for IndexMap where K: Hash + Eq, diff --git a/src/set.rs b/src/set.rs index 4560caa9..86ab0f90 100644 --- a/src/set.rs +++ b/src/set.rs @@ -11,7 +11,7 @@ use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; use core::iter::{Chain, FromIterator}; -use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; +use core::ops::{BitAnd, BitOr, BitXor, Index, RangeBounds, Sub}; use core::slice; use super::{Entries, Equivalent, IndexMap}; @@ -606,6 +606,16 @@ impl IndexSet { } } +impl Index for IndexSet { + type Output = T; + + /// ***Panics*** if `index` is out of bounds. + fn index(&self, index: usize) -> &T { + self.get_index(index) + .expect("IndexSet: index out of bounds") + } +} + /// An owning iterator over the items of a `IndexSet`. /// /// This `struct` is created by the [`into_iter`] method on [`IndexSet`] diff --git a/tests/quick.rs b/tests/quick.rs index f17fff86..10e11b52 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -1,4 +1,4 @@ -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use quickcheck::quickcheck; @@ -163,6 +163,26 @@ quickcheck! { elements.iter().all(|k| map.get(k).is_some()) } + fn indexing(insert: Vec) -> bool { + let mut map: IndexMap<_, _> = insert.into_iter().map(|x| (x, x)).collect(); + let set: IndexSet<_> = map.keys().cloned().collect(); + assert_eq!(map.len(), set.len()); + + for (i, &key) in set.iter().enumerate() { + assert_eq!(map.get_index(i), Some((&key, &key))); + assert_eq!(set.get_index(i), Some(&key)); + assert_eq!(map[i], key); + assert_eq!(set[i], key); + + *map.get_index_mut(i).unwrap().1 >>= 1; + map[i] <<= 1; + } + + set.iter().enumerate().all(|(i, &key)| { + let value = key & !1; + map[&key] == value && map[i] == value + }) + } } use crate::Op::*; From 4d4fc0a1c8aa522139e1f88ea626941001d0e50a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 6 Aug 2020 12:15:44 -0700 Subject: [PATCH 2/2] Add documentation and examples for Index and IndexMut --- src/map.rs | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/set.rs | 30 +++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/src/map.rs b/src/map.rs index f4ef97c8..6829ef94 100644 --- a/src/map.rs +++ b/src/map.rs @@ -993,6 +993,28 @@ impl IntoIterator for IndexMap { } } +/// Access `IndexMap` values corresponding to a key. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_uppercase()); +/// } +/// assert_eq!(map["lorem"], "LOREM"); +/// assert_eq!(map["ipsum"], "IPSUM"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// println!("{:?}", map["bar"]); // panics! +/// ``` impl Index<&Q> for IndexMap where Q: Hash + Equivalent, @@ -1001,31 +1023,90 @@ where { type Output = V; + /// Returns a reference to the value corresponding to the supplied `key`. + /// /// ***Panics*** if `key` is not present in the map. fn index(&self, key: &Q) -> &V { self.get(key).expect("IndexMap: key not found") } } +/// Access `IndexMap` values corresponding to a key. +/// /// Mutable indexing allows changing / updating values of key-value /// pairs that are already present. /// /// You can **not** insert new pairs with index syntax, use `.insert()`. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_string()); +/// } +/// let lorem = &mut map["lorem"]; +/// assert_eq!(lorem, "Lorem"); +/// lorem.retain(char::is_lowercase); +/// assert_eq!(map["lorem"], "orem"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// map["bar"] = 1; // panics! +/// ``` impl IndexMut<&Q> for IndexMap where Q: Hash + Equivalent, K: Hash + Eq, S: BuildHasher, { + /// Returns a mutable reference to the value corresponding to the supplied `key`. + /// /// ***Panics*** if `key` is not present in the map. fn index_mut(&mut self, key: &Q) -> &mut V { self.get_mut(key).expect("IndexMap: key not found") } } +/// Access `IndexMap` values at indexed positions. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_uppercase()); +/// } +/// assert_eq!(map[0], "LOREM"); +/// assert_eq!(map[1], "IPSUM"); +/// map.reverse(); +/// assert_eq!(map[0], "AMET"); +/// assert_eq!(map[1], "SIT"); +/// map.sort_keys(); +/// assert_eq!(map[0], "AMET"); +/// assert_eq!(map[1], "DOLOR"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// println!("{:?}", map[10]); // panics! +/// ``` impl Index for IndexMap { type Output = V; + /// Returns a reference to the value at the supplied `index`. + /// /// ***Panics*** if `index` is out of bounds. fn index(&self, index: usize) -> &V { self.get_index(index) @@ -1034,11 +1115,38 @@ impl Index for IndexMap { } } +/// Access `IndexMap` values at indexed positions. +/// /// Mutable indexing allows changing / updating indexed values /// that are already present. /// /// You can **not** insert new values with index syntax, use `.insert()`. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_string()); +/// } +/// let lorem = &mut map[0]; +/// assert_eq!(lorem, "Lorem"); +/// lorem.retain(char::is_lowercase); +/// assert_eq!(map["lorem"], "orem"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// map[10] = 1; // panics! +/// ``` impl IndexMut for IndexMap { + /// Returns a mutable reference to the value at the supplied `index`. + /// /// ***Panics*** if `index` is out of bounds. fn index_mut(&mut self, index: usize) -> &mut V { self.get_index_mut(index) diff --git a/src/set.rs b/src/set.rs index 86ab0f90..99493891 100644 --- a/src/set.rs +++ b/src/set.rs @@ -606,9 +606,39 @@ impl IndexSet { } } +/// Access `IndexSet` values at indexed positions. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexSet; +/// +/// let mut set = IndexSet::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// set.insert(word.to_string()); +/// } +/// assert_eq!(set[0], "Lorem"); +/// assert_eq!(set[1], "ipsum"); +/// set.reverse(); +/// assert_eq!(set[0], "amet"); +/// assert_eq!(set[1], "sit"); +/// set.sort(); +/// assert_eq!(set[0], "Lorem"); +/// assert_eq!(set[1], "amet"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexSet; +/// +/// let mut set = IndexSet::new(); +/// set.insert("foo"); +/// println!("{:?}", set[10]); // panics! +/// ``` impl Index for IndexSet { type Output = T; + /// Returns a reference to the value at the supplied `index`. + /// /// ***Panics*** if `index` is out of bounds. fn index(&self, index: usize) -> &T { self.get_index(index)