Loading page…
Rust walkthroughs
Loading page…
indexmap::IndexMap::shift_insert enable positional insertion unlike standard maps?indexmap::IndexMap::shift_insert enables insertion at a specific index position while shifting all subsequent entries to make room—a capability impossible with standard HashMap or BTreeMap which provide no ordering guarantees, and distinct from IndexMap's regular insert which either replaces existing entries or appends to the end. This positional control allows you to maintain a specific order of entries based on application requirements, not just insertion order, enabling patterns like priority-based insertion, alphabetically sorted insertion at specific positions, or building ordered maps where entry position carries semantic meaning beyond the key-value relationship.
use std::collections::HashMap;
use indexmap::IndexMap;
fn main() {
// HashMap: no ordering guarantees
let mut hashmap = HashMap::new();
hashmap.insert("c", 3);
hashmap.insert("a", 1);
hashmap.insert("b", 2);
println!("HashMap iteration order is undefined:");
for (k, v) in &hashmap {
println!(" {}: {}", k, v); // Order varies
}
// IndexMap: preserves insertion order
let mut indexmap = IndexMap::new();
indexmap.insert("c", 3);
indexmap.insert("a", 1);
indexmap.insert("b", 2);
println!("\nIndexMap preserves insertion order:");
for (k, v) in &indexmap {
println!(" {}: {}", k, v); // c, a, b
}
}HashMap provides no ordering; IndexMap preserves insertion order by default.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
// Regular insert appends to end
map.insert("first", 1);
map.insert("second", 2);
map.insert("third", 3);
println!("After inserts:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// Insert with existing key replaces value, keeps position
map.insert("second", 20);
println!("\nAfter replacing 'second':");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// "second" stays at index 1, value changes to 20
}Regular insert appends new entries and replaces existing entries in-place.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("c", 3);
map.insert("e", 5);
println!("Initial state:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// Index 0: a = 1
// Index 1: c = 3
// Index 2: e = 5
// Insert "b" at index 1, shifting c and e
map.shift_insert(1, "b", 2);
println!("\nAfter shift_insert(1, 'b', 2):");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// Index 0: a = 1
// Index 1: b = 2
// Index 2: c = 3
// Index 3: e = 5
// Insert "d" at index 3
map.shift_insert(3, "d", 4);
println!("\nAfter shift_insert(3, 'd', 4):");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// Index 0: a = 1
// Index 1: b = 2
// Index 2: c = 3
// Index 3: d = 4
// Index 4: e = 5
}shift_insert inserts at the specified index, shifting all entries at that index and after to make room.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("x", 10);
map.insert("y", 20);
map.insert("z", 30);
// Insert at beginning
map.shift_insert(0, "w", 0);
println!("After inserting 'w' at index 0:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// w, x, y, z (all shifted right)
// Insert at end (same as regular insert)
map.shift_insert(4, "end", 100);
println!("\nAfter inserting 'end' at index 4:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// w, x, y, z, end
// Insert in middle
map.shift_insert(2, "middle", 50);
println!("\nAfter inserting 'middle' at index 2:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// w, x, middle, y, z, end
}Entries at and after the insertion index are shifted right by one position.
use indexmap::IndexMap;
fn main() {
let mut map1 = IndexMap::new();
let mut map2 = IndexMap::new();
// Both start the same
map1.insert("a", 1);
map1.insert("c", 3);
map2.insert("a", 1);
map2.insert("c", 3);
// insert with new key: appends to end
map1.insert("b", 2);
println!("After insert('b', 2):");
for (i, (k, v)) in map1.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// a, c, b (appended)
// shift_insert: places at specific position
map2.shift_insert(1, "b", 2);
println!("\nAfter shift_insert(1, 'b', 2):");
for (i, (k, v)) in map2.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// a, b, c (alphabetically ordered)
// insert with existing key: replaces in place
map1.insert("a", 10);
println!("\nAfter insert('a', 10) on map1:");
for (i, (k, v)) in map1.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// a stays at index 0, value changes to 10
// shift_insert with existing key: moves to new position
// Note: shift_insert with existing key behaves differently!
// It actually panics if the key exists.
}insert appends or replaces in-place; shift_insert inserts at a position and shifts.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// shift_insert panics if key already exists
// This would panic:
// map.shift_insert(0, "b", 20);
// panic: "key already exists"
// To move an existing entry, use swap_indices or shift_remove + shift_insert
let old_index = map.get_index_of("b").unwrap(); // 1
let value = map.shift_remove("b").unwrap();
map.shift_insert(0, "b", value);
println!("After moving 'b' to front:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// b, a, c
}shift_insert panics if the key already exists; to reposition, remove and re-insert.
use indexmap::IndexMap;
fn insert_sorted(map: &mut IndexMap<&str, i32>, key: &str, value: i32) {
// Find correct position to maintain alphabetical order
let pos = map.iter()
.position(|(k, _)| *k > key)
.unwrap_or(map.len());
map.shift_insert(pos, key, value);
}
fn main() {
let mut map = IndexMap::new();
insert_sorted(&mut map, "delta", 4);
insert_sorted(&mut map, "alpha", 1);
insert_sorted(&mut map, "charlie", 3);
insert_sorted(&mut map, "bravo", 2);
println!("Sorted order:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// alpha, bravo, charlie, delta
}Use shift_insert to maintain sorted order dynamically.
use indexmap::IndexMap;
#[derive(Debug)]
struct Task {
name: String,
priority: u8, // Lower number = higher priority
}
fn insert_by_priority(map: &mut IndexMap<String, Task>, task: Task) {
let pos = map.iter()
.position(|(_, t)| t.priority > task.priority)
.unwrap_or(map.len());
map.shift_insert(pos, task.name.clone(), task);
}
fn main() {
let mut tasks = IndexMap::new();
insert_by_priority(&mut tasks, Task { name: "email".to_string(), priority: 5 });
insert_by_priority(&mut tasks, Task { name: "backup".to_string(), priority: 2 });
insert_by_priority(&mut tasks, Task { name: "log".to_string(), priority: 8 });
insert_by_priority(&mut tasks, Task { name: "critical".to_string(), priority: 1 });
println!("Tasks by priority:");
for (i, (name, task)) in tasks.iter().enumerate() {
println!(" {}: {:?} ", i, task);
}
// critical (1), backup (2), email (5), log (8)
}Position insertion enables priority queues with O(n) insertion.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
println!("Initial indices:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// Access by index
println!("\nAccess by index:");
println!(" map[0] = {:?}", map.get_index(0)); // Some((&"a", &1))
println!(" map[2] = {:?}", map.get_index(2)); // Some((&"c", &3))
// Indices after shift_insert
map.shift_insert(1, "x", 10);
println!("\nAfter shift_insert(1, 'x', 10):");
println!(" map[0] = {:?}", map.get_index(0)); // "a"
println!(" map[1] = {:?}", map.get_index(1)); // "x"
println!(" map[2] = {:?}", map.get_index(2)); // "b"
println!(" map[3] = {:?}", map.get_index(3)); // "c"
println!(" map[4] = {:?}", map.get_index(4)); // "d"
// Keys to indices changed
println!("\nKey positions:");
println!(" 'a' at index {:?}", map.get_index_of("a")); // 0
println!(" 'b' at index {:?}", map.get_index_of("b")); // 2 (shifted)
println!(" 'c' at index {:?}", map.get_index_of("c")); // 3 (shifted)
}shift_insert shifts indices for entries at and after the insertion point.
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Move "d" from position 3 to position 0
let value = map.shift_remove("d").unwrap();
println!("Removed 'd': {}", value);
map.shift_insert(0, "d", value);
println!("\nAfter moving 'd' to front:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// d, a, b, c
// Move "a" from position 1 to end
let value = map.shift_remove("a").unwrap();
map.shift_insert(3, "a", value); // len is 3 after remove
println!("\nAfter moving 'a' to end:");
for (i, (k, v)) in map.iter().enumerate() {
println!(" Index {}: {} = {}", i, k, v);
}
// d, b, c, a
}Combine shift_remove and shift_insert to move entries within the map.
use indexmap::IndexMap;
fn main() {
// shift_insert is O(n) because it shifts entries
let mut map = IndexMap::new();
// Building a map by appending (O(1) per insert)
for i in 0..1000 {
map.insert(format!("key{}", i), i);
}
// Inserting at beginning requires shifting all entries (O(n))
// This is slower than append
map.shift_insert(0, "new_first".to_string(), -1);
// Inserting near the end is faster (fewer shifts)
map.shift_insert(900, "near_end".to_string(), 9000);
// Regular insert at end is O(1)
map.insert("last".to_string(), 1000);
println!("Map size: {}", map.len());
// For comparison, HashMap insertion is O(1) average
// but HashMap has no ordering
}shift_insert is O(n) due to index shifting; regular insert is O(1) amortized.
use indexmap::IndexMap;
#[derive(Debug)]
struct ConfigEntry {
key: String,
value: String,
source: String, // "default", "env", "file", "cli"
}
fn main() {
let mut config: IndexMap<String, ConfigEntry> = IndexMap::new();
// Default values at the beginning
config.shift_insert(0, "port".to_string(), ConfigEntry {
key: "port".to_string(),
value: "8080".to_string(),
source: "default".to_string(),
});
config.shift_insert(1, "host".to_string(), ConfigEntry {
key: "host".to_string(),
value: "localhost".to_string(),
source: "default".to_string(),
});
// Environment variables override (insert before defaults)
config.shift_insert(0, "host".to_string(), ConfigEntry {
key: "host".to_string(),
value: "0.0.0.0".to_string(),
source: "env".to_string(),
});
// Config file values (insert at specific priority position)
// This would panic since "host" already exists - need to handle conflicts
// The order shows precedence for debugging
println!("Configuration (in precedence order):");
for (i, (key, entry)) in config.iter().enumerate() {
println!(" {}: {} = {} (from {})", i, key, entry.value, entry.source);
}
}Position insertion enables priority-based ordering for configuration layers.
use indexmap::IndexMap;
#[derive(Debug, Clone)]
struct Song {
title: String,
artist: String,
}
fn main() {
let mut playlist: IndexMap<String, Song> = IndexMap::new();
// Add songs
playlist.insert("song1".to_string(), Song {
title: "First".to_string(),
artist: "Artist A".to_string(),
});
playlist.insert("song2".to_string(), Song {
title: "Second".to_string(),
artist: "Artist B".to_string(),
});
playlist.insert("song3".to_string(), Song {
title: "Third".to_string(),
artist: "Artist C".to_string(),
});
// Insert a song at position 1 (second in playlist)
playlist.shift_insert(1, "new_song".to_string(), Song {
title: "New Song".to_string(),
artist: "Artist D".to_string(),
});
// Move a song to the beginning (now playing)
let song = playlist.shift_remove("song3").unwrap();
playlist.shift_insert(0, "song3".to_string(), song);
println!("Playlist order:");
for (i, (id, song)) in playlist.iter().enumerate() {
println!(" {}: {} by {}", i + 1, song.title, song.artist);
}
}shift_insert enables playlist reordering where position matters.
Method comparison:
| Method | Behavior | Complexity |
|--------|----------|------------|
| insert | Append new key, replace existing in-place | O(1) amortized |
| shift_insert | Insert at index, shift entries right | O(n) |
IndexMap ordering capabilities:
| Operation | Standard HashMap | BTreeMap | IndexMap |
|-----------|------------------|----------|----------|
| Preserves insertion order | No | No | Yes |
| Access by index | No | No | Yes |
| Insert at position | N/A | N/A | shift_insert |
| Move entry to position | N/A | N/A | shift_remove + shift_insert |
Key insight: IndexMap::shift_insert provides positional control that standard maps fundamentally cannot offer—HashMap has no ordering, and BTreeMap maintains key-based ordering with no application control. This enables patterns where entry position carries semantic meaning: priority queues where position indicates processing order, configuration layers where earlier entries take precedence, playlists where position is the primary concern, and any scenario where you need to maintain a specific order independent of key sorting. The cost is O(n) for shifting, acceptable for moderate sizes or when position changes are infrequent. The method panics if the key already exists, requiring shift_remove first to reposition entries, which ensures no duplicate keys and maintains index consistency. Use shift_insert when position matters; use regular insert when you only need key-value lookup with insertion order preservation.