Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
bench: test build benchmarks

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
target: thumbv6m-none-eabi

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
Expand All @@ -85,7 +85,7 @@ jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@beta
with:
components: clippy
Expand All @@ -94,7 +94,7 @@ jobs:
miri:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@nightly
with:
components: miri, rust-src
Expand All @@ -110,7 +110,7 @@ jobs:
name: Check MSRV and minimal-versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/rust-toolchain@1.85.0 # MSRV
- uses: taiki-e/install-action@v2
Expand Down
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "indexmap"
edition = "2024"
version = "2.14.0"
version = "2.14.1"
documentation = "https://docs.rs/indexmap/"
repository = "https://github.com/indexmap-rs/indexmap"
license = "Apache-2.0 OR MIT"
Expand Down Expand Up @@ -62,15 +62,15 @@ rustdoc-args = ["--cfg", "docsrs"]
members = ["test-nostd", "test-serde", "test-sval"]

[lints.rust]
private-bounds = "deny"
private-interfaces = "deny"
unnameable-types = "deny"
unreachable-pub = "deny"
private_bounds = "deny"
private_interfaces = "deny"
unnameable_types = "deny"
unreachable_pub = "deny"

# We *mostly* avoid unsafe code, but there are a few fine-grained cases allowed
unsafe-code = "deny"
unsafe_code = "deny"

rust-2018-idioms = "warn"
rust_2018_idioms = { level = "warn", priority = -1 }

[lints.clippy]
style = "allow"
6 changes: 6 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Releases

## 2.14.1 (2026-08-28)

- Simplify comparisons where `Equivalent` isn't needed (`Q = K`).
- Unify index assertions for bounds checks.
- Fix (or `expect`) clippy lints.

## 2.14.0 (2026-04-09)

- **MSRV**: Rust 1.85.0 or later is now required.
Expand Down
2 changes: 2 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ fn lookup_hashmap_10_000_exist(b: &mut Bencher) {
}
b.iter(|| {
let mut found = 0;
#[expect(clippy::unnecessary_get_then_check)]
for key in 5000..c {
found += map.get(&key).is_some() as i32;
}
Expand All @@ -306,6 +307,7 @@ fn lookup_hashmap_10_000_noexist(b: &mut Bencher) {
}
b.iter(|| {
let mut found = 0;
#[expect(clippy::unnecessary_get_then_check)]
for key in c..15000 {
found += map.get(&key).is_some() as i32;
}
Expand Down
2 changes: 2 additions & 0 deletions benches/faststring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ fn lookup_hashmap_10_000_exist_string(b: &mut Bencher) {
let lookups = (5000..c).map(|x| x.to_string()).collect::<Vec<_>>();
b.iter(|| {
let mut found = 0;
#[expect(clippy::unnecessary_get_then_check)]
for key in &lookups {
found += map.get(key).is_some() as i32;
}
Expand All @@ -137,6 +138,7 @@ fn lookup_hashmap_10_000_exist_string_oneshot(b: &mut Bencher) {
.collect::<Vec<_>>();
b.iter(|| {
let mut found = 0;
#[expect(clippy::unnecessary_get_then_check)]
for key in &lookups {
found += map.get(key).is_some() as i32;
}
Expand Down
1 change: 1 addition & 0 deletions src/map/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ mod tests {
let slice = map.as_slice();

// RangeFull
#[expect(clippy::redundant_slicing)]
check(&vec[..], &map[..], &slice[..]);

for i in 0usize..10 {
Expand Down
62 changes: 24 additions & 38 deletions src/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ fn insert() {

#[test]
fn insert_full() {
let insert = vec![9, 2, 7, 1, 4, 6, 13];
let present = vec![1, 6, 2];
let insert = [9, 2, 7, 1, 4, 6, 13];
let present = [1, 6, 2];
let mut map = IndexMap::with_capacity(insert.len());

for (i, &elt) in insert.iter().enumerate() {
Expand Down Expand Up @@ -207,10 +207,10 @@ fn reserve() {
assert_eq!(map.capacity(), capacity);
assert_eq!(map.get(&i), Some(&(i * i)));
}
map.insert(capacity, std::usize::MAX);
map.insert(capacity, usize::MAX);
assert_eq!(map.len(), capacity + 1);
assert!(map.capacity() > capacity);
assert_eq!(map.get(&capacity), Some(&std::usize::MAX));
assert_eq!(map.get(&capacity), Some(&usize::MAX));
}

#[test]
Expand All @@ -230,7 +230,7 @@ fn shrink_to_fit() {
assert_eq!(map.len(), i);
map.insert(i, i * i);
assert_eq!(map.len(), i + 1);
assert!(map.capacity() >= i + 1);
assert!(map.capacity() > i);
assert_eq!(map.get(&i), Some(&(i * i)));
map.shrink_to_fit();
assert_eq!(map.len(), i + 1);
Expand Down Expand Up @@ -327,11 +327,11 @@ fn partial_eq_and_eq() {
#[test]
fn extend() {
let mut map = IndexMap::new();
map.extend(vec![(&1, &2), (&3, &4)]);
map.extend(vec![(5, 6)]);
map.extend([(&1, &2), (&3, &4)]);
map.extend([(5, 6)]);
assert_eq!(
map.into_iter().collect::<Vec<_>>(),
vec![(1, 2), (3, 4), (5, 6)]
[(1, 2), (3, 4), (5, 6)]
);
}

Expand Down Expand Up @@ -374,18 +374,13 @@ fn entry_and_modify() {
fn entry_or_default() {
let mut map = IndexMap::new();

#[derive(Debug, PartialEq)]
#[derive(Debug, Default, PartialEq)]
enum TestEnum {
#[default]
DefaultValue,
NonDefaultValue,
}

impl Default for TestEnum {
fn default() -> Self {
TestEnum::DefaultValue
}
}

map.insert(1, TestEnum::NonDefaultValue);
assert_eq!(&mut TestEnum::NonDefaultValue, map.entry(1).or_default());

Expand Down Expand Up @@ -485,8 +480,7 @@ fn from_entries() {

#[test]
fn keys() {
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
let map: IndexMap<_, _> = vec.into_iter().collect();
let map = IndexMap::<_, _>::from_iter([(1, 'a'), (2, 'b'), (3, 'c')]);
let keys: Vec<_> = map.keys().copied().collect();
assert_eq!(keys.len(), 3);
assert!(keys.contains(&1));
Expand All @@ -496,8 +490,7 @@ fn keys() {

#[test]
fn into_keys() {
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
let map: IndexMap<_, _> = vec.into_iter().collect();
let map = IndexMap::<_, _>::from_iter([(1, 'a'), (2, 'b'), (3, 'c')]);
let keys: Vec<i32> = map.into_keys().collect();
assert_eq!(keys.len(), 3);
assert!(keys.contains(&1));
Expand All @@ -507,8 +500,7 @@ fn into_keys() {

#[test]
fn values() {
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
let map: IndexMap<_, _> = vec.into_iter().collect();
let map = IndexMap::<_, _>::from_iter([(1, 'a'), (2, 'b'), (3, 'c')]);
let values: Vec<_> = map.values().copied().collect();
assert_eq!(values.len(), 3);
assert!(values.contains(&'a'));
Expand All @@ -518,8 +510,7 @@ fn values() {

#[test]
fn values_mut() {
let vec = vec![(1, 1), (2, 2), (3, 3)];
let mut map: IndexMap<_, _> = vec.into_iter().collect();
let mut map = IndexMap::<_, _>::from_iter([(1, 1), (2, 2), (3, 3)]);
for value in map.values_mut() {
*value *= 2
}
Expand All @@ -532,8 +523,7 @@ fn values_mut() {

#[test]
fn into_values() {
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
let map: IndexMap<_, _> = vec.into_iter().collect();
let map = IndexMap::<_, _>::from_iter([(1, 'a'), (2, 'b'), (3, 'c')]);
let values: Vec<char> = map.into_values().collect();
assert_eq!(values.len(), 3);
assert!(values.contains(&'a'));
Expand Down Expand Up @@ -698,8 +688,7 @@ fn shift_remove_full() {

#[test]
fn sorted_unstable_by() {
let mut map: IndexMap<i32, i32> = IndexMap::new();
map.extend(vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);
let map = IndexMap::<i32, i32>::from_iter([(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);
let sorted = map.sorted_unstable_by(|_a, b, _c, d| d.cmp(&b));

assert_eq!(
Expand Down Expand Up @@ -750,28 +739,24 @@ fn insert_before_oob() {

#[test]
fn clear() {
let mut map: IndexMap<i32, i32> = IndexMap::new();
map.extend(vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);
let mut map = IndexMap::<i32, i32>::from_iter([(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);
assert_ne!(map.len(), 0);
map.clear();
assert_eq!(map.len(), 0);
}

#[test]
fn get_range() {
let mut index_map: IndexMap<i32, i32> = IndexMap::new();
index_map.insert(1, 10);
index_map.insert(2, 20);
index_map.insert(3, 30);
index_map.insert(4, 40);
index_map.insert(5, 50);
let map = IndexMap::<i32, i32>::from_iter([(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);

let result = index_map.get_range(2..2);
let result = map.get_range(2..2);
assert!(result.unwrap().is_empty());

let result = index_map.get_range(4..2);
#[expect(clippy::reversed_empty_ranges)]
let result = map.get_range(4..2);
assert!(result.is_none());

let result = index_map.get_range(2..4);
let result = map.get_range(2..4);
let slice: &Slice<i32, i32> = result.unwrap();
assert_eq!(slice.len(), 2);
assert_eq!(slice, &[(3, 30), (4, 40)]);
Expand All @@ -789,6 +774,7 @@ fn get_range_mut() {
let result = index_map.get_range_mut(2..2);
assert!(result.unwrap().is_empty());

#[expect(clippy::reversed_empty_ranges)]
let result = index_map.get_range_mut(4..2);
assert!(result.is_none());

Expand Down
1 change: 1 addition & 0 deletions src/set/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ mod tests {
let slice = set.as_slice();

// RangeFull
#[expect(clippy::redundant_slicing)]
check(&vec[..], &set[..], &slice[..]);

for i in 0usize..10 {
Expand Down
23 changes: 11 additions & 12 deletions src/set/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ fn insert() {

#[test]
fn insert_full() {
let insert = vec![9, 2, 7, 1, 4, 6, 13];
let present = vec![1, 6, 2];
let insert = [9, 2, 7, 1, 4, 6, 13];
let present = [1, 6, 2];
let mut set = IndexSet::with_capacity(insert.len());

for (i, &elt) in insert.iter().enumerate() {
Expand Down Expand Up @@ -90,8 +90,7 @@ fn insert_2() {

#[test]
fn insert_dup() {
let mut elements = vec![0, 2, 4, 6, 8];
let mut set: IndexSet<u8> = elements.drain(..).collect();
let mut set = IndexSet::<u8>::from_iter([0, 2, 4, 6, 8]);
{
let (i, v) = set.get_full(&0).unwrap();
assert_eq!(set.len(), 5);
Expand Down Expand Up @@ -175,8 +174,8 @@ fn replace() {

#[test]
fn replace_full() {
let replace = vec![9, 2, 7, 1, 4, 6, 13];
let present = vec![1, 6, 2];
let replace = [9, 2, 7, 1, 4, 6, 13];
let present = [1, 6, 2];
let mut set = IndexSet::with_capacity(replace.len());

for (i, &elt) in replace.iter().enumerate() {
Expand Down Expand Up @@ -223,8 +222,7 @@ fn replace_2() {

#[test]
fn replace_dup() {
let mut elements = vec![0, 2, 4, 6, 8];
let mut set: IndexSet<u8> = elements.drain(..).collect();
let mut set = IndexSet::<u8>::from_iter([0, 2, 4, 6, 8]);
{
let (i, v) = set.get_full(&0).unwrap();
assert_eq!(set.len(), 5);
Expand Down Expand Up @@ -338,7 +336,7 @@ fn shrink_to_fit() {
assert_eq!(set.len(), i);
set.insert(i);
assert_eq!(set.len(), i + 1);
assert!(set.capacity() >= i + 1);
assert!(set.capacity() > i);
assert_eq!(set.get(&i), Some(&i));
set.shrink_to_fit();
assert_eq!(set.len(), i + 1);
Expand Down Expand Up @@ -426,9 +424,9 @@ fn partial_eq_and_eq() {
#[test]
fn extend() {
let mut set = IndexSet::new();
set.extend(vec![&1, &2, &3, &4]);
set.extend(vec![5, 6]);
assert_eq!(set.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6]);
set.extend([&1, &2, &3, &4]);
set.extend([5, 6]);
assert_eq!(set.into_iter().collect::<Vec<_>>(), [1, 2, 3, 4, 5, 6]);
}

#[test]
Expand Down Expand Up @@ -852,6 +850,7 @@ fn get_range() {
let result = set.get_range(0..0);
assert_eq!(result.unwrap().len(), 0);

#[expect(clippy::reversed_empty_ranges)]
let result = set.get_range(2..1);
assert!(result.is_none());
}
Expand Down
4 changes: 1 addition & 3 deletions tests/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,9 +796,7 @@ quickcheck_limit! {
// value seen for that key!
let mut last_val_per_key = HashMap::new();
for &(k, v) in input.iter().rev() {
if !last_val_per_key.contains_key(&k) {
last_val_per_key.insert(k, v);
}
last_val_per_key.entry(k).or_insert(v);
}

// iterate over the keys in (A) in order, and match each one with
Expand Down