Skip to content

Add shift_remove family to IndexMap/IndexSet - #685

Merged
sgued merged 3 commits into
rust-embedded:mainfrom
HarnageaGabriel:feat/shift-remove-indexmap
Oct 6, 2026
Merged

sgued merged 3 commits into
rust-embedded:mainfrom
HarnageaGabriel:feat/shift-remove-indexmap

Conversation

@HarnageaGabriel

Copy link
Copy Markdown
Contributor

Summary

swap_remove is O(1) but perturbs map/set order by moving the last element into the removed slot. This adds order-preserving removal, mirroring the indexmap crate's shift_remove API:

  • IndexMap::shift_remove / shift_remove_entry / shift_remove_index
  • OccupiedEntry::shift_remove / shift_remove_entry
  • IndexSet::shift_remove

Implementation: CoreMap::shift_remove_found removes the entry with Vec::remove (order-preserving shift) instead of swap_remove_unchecked, then walks the indices robin-hood table decrementing every Pos whose index pointed past the removed slot, then runs the existing backward_shift_after_removal to restore the probe-distance invariant. O(n) instead of swap_remove's O(1), same as upstream indexmap.

remove/swap_remove/remove_entry/swap_remove_entry are untouched.

Fixes #678

Test plan

  • cargo build
  • cargo test --lib (251 passed, including 5 new shift_remove tests covering order preservation on IndexMap, OccupiedEntry, shift_remove_index, and IndexSet)
  • cargo test --doc index_map (31 passed, including new doctests)
  • cargo clippy --lib clean (repo denies clippy::undocumented_unsafe_blocks)
  • cargo fmt --check clean

@sgued sgued left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, sorry for the delayed review.

Regarding your additions, LGTM besides a couple of nitpicks.

Can I also ask you to also rename the remove_found function to swap_remove_found ?
That way it's clear in future refactors that remove_found might have unexpected behaviour.

Comment thread src/index_map.rs
Comment on lines +746 to +747
/// preserving their relative order.
pub fn shift_remove_entry(self) -> (K, V) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// preserving their relative order.
pub fn shift_remove_entry(self) -> (K, V) {
/// preserving their relative order.
///
/// Computes in **O(n)** time on average
pub fn shift_remove_entry(self) -> (K, V) {

Comment thread src/index_map.rs Outdated
/// preserving their relative order.
pub fn shift_remove_entry(self) -> (K, V) {
// SAFETY: We know that `pos` is valid from the creation of the entry
// and that cannot have changed since we held a mutable entry to the map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// and that cannot have changed since we held a mutable entry to the map
// and that cannot have changed since we held a mutable reference to the map

Comment thread src/index_map.rs
Comment on lines +809 to +810
/// preserving their relative order.
pub fn shift_remove(self) -> V {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// preserving their relative order.
pub fn shift_remove(self) -> V {
/// preserving their relative order.
///
/// Computes in **O(n)** time on average
pub fn shift_remove(self) -> V {

swap_remove is O(1) but perturbs the position of the last element,
which isn't acceptable when callers need remaining entries to keep
their relative insertion order after a removal. This mirrors the
upstream indexmap crate's shift_remove/shift_remove_entry API
(O(n), shifts entries down via Vec::remove and fixes up the hash
table's index pointers) adapted to heapless's fixed-capacity,
no_std CoreMap/Pos representation.

Fixes rust-embedded#678
…n) shift_remove

Signed-off-by: HarnageaGabriel <gabriel.harnagea06@gmail.com>
@sgued
sgued force-pushed the feat/shift-remove-indexmap branch from 7c2452f to 4a0db9e Compare October 6, 2026 19:08
@sgued
sgued enabled auto-merge October 6, 2026 19:08
@sgued
sgued added this pull request to the merge queue Oct 6, 2026
Merged via the queue into rust-embedded:main with commit b6bbc3d Oct 6, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IndexMap/IndexSet: implement shift_remove and variants

2 participants