How do I use ordered hash maps in Rust?

Walkthrough

The indexmap crate provides IndexMap and IndexSet — hash-based data structures that preserve insertion order. Unlike HashMap and HashSet from the standard library, IndexMap iterates in the order elements were inserted. It also supports indexing by position, efficient iteration, and all the usual map operations. This is useful when you need deterministic ordering, JSON-like data structures, or want to iterate in insertion order.

Key features:

  1. IndexMap<K, V> — ordered hash map (preserves insertion order)
  2. IndexSet<T> — ordered hash set (preserves insertion order)
  3. Position-based indexing — access by numeric index, not just key
  4. Insertion order iteration — iterate in the order items were added
  5. Efficient operations — O(1) lookups like HashMap
  6. swap_remove vs shift_remove — different removal semantics

Code Example

# Cargo.toml
[dependencies]
indexmap = "2"
use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    
    // Insert in specific order
    map.insert("apple", 3);
    map.insert("banana", 2);
    map.insert("cherry", 5);
    
    // Iterates in insertion order
    for (key, value) in &map {
        println!("{}: {}", key, value);
    }
    // Output: apple: 3, banana: 2, cherry: 5
}

Creating IndexMaps

use indexmap::IndexMap;
 
fn main() {
    // Create empty map
    let mut map: IndexMap<&str, i32> = IndexMap::new();
    
    // With capacity
    let mut map: IndexMap<&str, i32> = IndexMap::with_capacity(10);
    println!("Capacity: {}", map.capacity());
    
    // From iterator
    let map: IndexMap<&str, i32> = [
        ("one", 1),
        ("two", 2),
        ("three", 3),
    ].into_iter().collect();
    
    println!("Map: {:?}", map);
    
    // From array
    let map: IndexMap<_, _> = IndexMap::from([
        ("a", 1),
        ("b", 2),
        ("c", 3),
    ]);
    println!("From array: {:?}", map);
    
    // Builder pattern
    let map = IndexMap::<&str, i32>::new()
        .into_iter()
        .collect::<IndexMap<_, _>>();
}

Basic Operations

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    
    // Insert
    map.insert("apple", 3);
    map.insert("banana", 2);
    map.insert("cherry", 5);
    
    println!("Map: {:?}", map);
    
    // Get by key
    let apple = map.get("apple");
    println!("Apple: {:?}", apple);
    
    // Get mutable
    if let Some(value) = map.get_mut("banana") {
        *value += 10;
    }
    println!("After mutation: {:?}", map);
    
    // Contains key
    println!("Contains apple: {}", map.contains_key("apple"));
    println!("Contains grape: {}", map.contains_key("grape"));
    
    // Length
    println!("Length: {}", map.len());
    
    // Is empty
    println!("Is empty: {}", map.is_empty());
    
    // Entry API
    map.entry("apple").and_modify(|v| *v += 1).or_insert(100);
    println!("After entry (existing): {:?}", map.get("apple"));
    
    map.entry("grape").or_insert(4);
    println!("After entry (new): {:?}", map.get("grape"));
    
    // Remove
    let removed = map.remove("banana");
    println!("Removed: {:?}", removed);
    println!("After remove: {:?}", map);
    
    // Clear
    map.clear();
    println!("After clear: {}", map.len());
}

Insertion Order Iteration

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    
    // Insert in specific order
    map.insert("first", 1);
    map.insert("second", 2);
    map.insert("third", 3);
    map.insert("fourth", 4);
    
    // Iterate in insertion order
    println!("Iteration order:");
    for (key, value) in &map {
        println!("  {} = {}", key, value);
    }
    
    // Iterate keys only
    println!("\nKeys:");
    for key in map.keys() {
        println!("  {}", key);
    }
    
    // Iterate values only
    println!("\nValues:");
    for value in map.values() {
        println!("  {}", value);
    }
    
    // Mutable iteration
    for value in map.values_mut() {
        *value *= 10;
    }
    println!("\nAfter modification: {:?}", map);
    
    // Into iteration (consumes map)
    let map: IndexMap<_, _> = [("a", 1), ("b", 2)].into_iter().collect();
    for (key, value) in map {
        println!("Consumed: {} = {}", key, value);
    }
}
 
// Comparison with HashMap
fn compare_with_hashmap() {
    use std::collections::HashMap;
    use indexmap::IndexMap;
    
    let mut std_map = HashMap::new();
    std_map.insert("c", 3);
    std_map.insert("a", 1);
    std_map.insert("b", 2);
    
    let mut index_map = IndexMap::new();
    index_map.insert("c", 3);
    index_map.insert("a", 1);
    index_map.insert("b", 2);
    
    println!("HashMap order (undefined): ");
    for (k, v) in &std_map {
        println!("  {} = {}", k, v);
    }
    
    println!("IndexMap order (insertion): ");
    for (k, v) in &index_map {
        println!("  {} = {}", k, v);
    }
}

Index-Based Access

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    map.insert("apple", 3);
    map.insert("banana", 2);
    map.insert("cherry", 5);
    
    // Get index of a key
    let idx = map.get_index_of("banana");
    println!("Index of banana: {:?}", idx);
    
    // Get key-value by index
    let (key, value) = map.get_index(1).unwrap();
    println!("Index 1: {} = {}", key, value);
    
    // Get mutable by index
    let (key, value) = map.get_index_mut(2).unwrap();
    *value += 100;
    println!("Modified index 2: {} = {}", key, value);
    
    // Get key at index
    let key = map.keys()[1];
    println!("Key at index 1: {}", key);
    
    // Get value at index (using Index)
    let value = &map["apple"];
    println!("Value for apple: {}", value);
    
    // Indices for iteration
    for (idx, (key, value)) in map.iter().enumerate() {
        println!("Index {}: {} = {}", idx, key, value);
    }
}
 
// First and last elements
fn first_last() {
    use indexmap::IndexMap;
    
    let mut map = IndexMap::new();
    map.insert("first", 1);
    map.insert("middle", 2);
    map.insert("last", 3);
    
    // First element
    if let Some((key, value)) = map.first() {
        println!("First: {} = {}", key, value);
    }
    
    // Last element
    if let Some((key, value)) = map.last() {
        println!("Last: {} = {}", key, value);
    }
}

Removing Elements

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    map.insert("apple", 1);
    map.insert("banana", 2);
    map.insert("cherry", 3);
    map.insert("date", 4);
    
    println!("Before: {:?}", map.keys().collect::<Vec<_>>());
    
    // swap_remove - swaps with last, doesn't shift indices
    // Fast O(1) but changes order
    map.swap_remove("banana");
    println!("After swap_remove: {:?}", map.keys().collect::<Vec<_>>());
    
    // Re-add for demonstration
    map.insert("banana", 2);
    println!("\nBefore: {:?}", map.keys().collect::<Vec<_>>());
    
    // shift_remove - shifts all elements after it
    // Preserves order but O(n)
    map.shift_remove("banana");
    println!("After shift_remove: {:?}", map.keys().collect::<Vec<_>>());
    
    // Remove by index
    let mut map2 = IndexMap::new();
    map2.insert("a", 1);
    map2.insert("b", 2);
    map2.insert("c", 3);
    
    // swap_remove_index
    let removed = map2.swap_remove_index(1);
    println!("\nRemoved index 1: {:?}", removed);
    println!("Remaining: {:?}", map2.keys().collect::<Vec<_>>());
    
    // shift_remove_index
    let mut map3 = IndexMap::new();
    map3.insert("a", 1);
    map3.insert("b", 2);
    map3.insert("c", 3);
    
    let removed = map3.shift_remove_index(1);
    println!("\nRemoved index 1: {:?}", removed);
    println!("Remaining: {:?}", map3.keys().collect::<Vec<_>>());
}
 
// Removal with indices
fn remove_with_index() {
    use indexmap::IndexMap;
    
    let mut map = IndexMap::new();
    map.insert("a", 1);
    map.insert("b", 2);
    map.insert("c", 3);
    
    // Remove and get index
    let (idx, key, value) = map.swap_remove_full("b").unwrap();
    println!("Removed at index {}: {} = {}", idx, key, value);
}

Entry API

use indexmap::IndexMap;
 
fn main() {
    let mut map: IndexMap<&str, i32> = IndexMap::new();
    
    // Basic entry operations
    map.entry("apple").or_insert(3);
    map.entry("banana").or_insert(2);
    println!("After or_insert: {:?}", map);
    
    // or_insert_with - lazy evaluation
    map.entry("cherry").or_insert_with(|| {
        println!("Computing cherry value");
        5
    });
    map.entry("cherry").or_insert_with(|| {
        println!("This won't print - cherry exists");
        10
    });
    println!("After or_insert_with: {:?}", map);
    
    // and_modify - modify existing value
    map.entry("apple").and_modify(|v| *v *= 2);
    map.entry("grape").and_modify(|v| *v *= 2).or_insert(1);
    println!("After and_modify: {:?}", map);
    
    // or_insert_with_key
    map.entry("date").or_insert_with_key(|key| {
        println!("Inserting key: {}", key);
        key.len() as i32
    });
    println!("After or_insert_with_key: {:?}", map);
    
    // or_default
    map.entry("elderberry").or_default();
    println!("After or_default: {:?}", map);
    
    // Check if occupied or vacant
    match map.entry("apple") {
        indexmap::map::Entry::Occupied(entry) => {
            println!("Apple is occupied with value: {}", entry.get());
        }
        indexmap::map::Entry::Vacant(entry) => {
            println!("Apple is vacant");
        }
    }
}
 
// Entry with index
fn entry_with_index() {
    use indexmap::IndexMap;
    
    let mut map: IndexMap<&str, i32> = IndexMap::new();
    
    // Get entry and its index
    let entry = map.entry("apple");
    let index = entry.index();
    map.entry("apple").or_insert(1);
    
    println!("Apple inserted at index: {}", index);
}

Updating and Modifying

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    
    // insert returns old value if key exists
    map.insert("apple", 3);
    let old = map.insert("apple", 5);
    println!("Old value: {:?}", old);
    println!("New value: {:?}", map.get("apple"));
    
    // insert_full returns (index, old_value)
    let (idx, old) = map.insert_full("apple", 10);
    println!("Index: {}, Old: {:?}", idx, old);
    
    // Get mutable reference
    if let Some(value) = map.get_mut("apple") {
        *value += 1;
    }
    println!("After get_mut: {:?}", map.get("apple"));
    
    // Update value in place
    map.entry("apple").and_modify(|v| *v *= 2);
    println!("After and_modify: {:?}", map.get("apple"));
    
    // Retain - keep only matching entries
    map.insert("banana", 2);
    map.insert("cherry", 5);
    map.insert("date", 8);
    
    map.retain(|_key, &mut value| value > 3);
    println!("After retain: {:?}", map);
    
    // Append - add all from another map
    let mut map2 = IndexMap::new();
    map2.insert("elderberry", 4);
    map2.insert("fig", 6);
    
    map.extend(map2);
    println!("After extend: {:?}", map.keys().collect::<Vec<_>>());
}
 
// Bulk updates
fn bulk_updates() {
    use indexmap::IndexMap;
    
    let mut map = IndexMap::new();
    map.insert("a", 1);
    map.insert("b", 2);
    map.insert("c", 3);
    
    // Update from iterator
    let updates = vec![
        ("a", 10),
        ("d", 4),  // New key
    ];
    
    for (key, value) in updates {
        map.insert(key, value);
    }
    
    println!("After updates: {:?}", map);
}

Sorting and Ordering

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    map.insert("cherry", 5);
    map.insert("apple", 3);
    map.insert("banana", 2);
    map.insert("date", 8);
    
    println!("Original order: {:?}", map.keys().collect::<Vec<_>>());
    
    // Sort by key
    map.sort_keys();
    println!("Sorted by key: {:?}", map.keys().collect::<Vec<_>>());
    
    // Sort by values (using sorted_by)
    let mut map2 = IndexMap::new();
    map2.insert("a", 3);
    map2.insert("b", 1);
    map2.insert("c", 2);
    
    map2.sort_by(|_k1, v1, _k2, v2| v1.cmp(v2));
    println!("Sorted by value: {:?}", map2.iter().collect::<Vec<_>>());
    
    // Sort unstable (faster, may not preserve equal element order)
    map2.sort_unstable_by(|_k1, v1, _k2, v2| v1.cmp(v2));
    
    // Reverse
    let mut map3 = IndexMap::new();
    map3.insert("a", 1);
    map3.insert("b", 2);
    map3.insert("c", 3);
    
    map3.reverse();
    println!("Reversed: {:?}", map3.keys().collect::<Vec<_>>());
}
 
// Sorting with custom comparator
fn custom_sorting() {
    use indexmap::IndexMap;
    
    let mut map = IndexMap::new();
    map.insert("apple", 10);
    map.insert("banana", 5);
    map.insert("cherry", 15);
    map.insert("date", 3);
    
    // Sort by value descending
    map.sort_by(|_k1, v1, _k2, v2| v2.cmp(v1));
    
    println!("Sorted by value (descending):");
    for (key, value) in &map {
        println!("  {} = {}", key, value);
    }
    
    // Sort by key length, then alphabetically
    let mut map2 = IndexMap::new();
    map2.insert("zz", 1);
    map2.insert("a", 2);
    map2.insert("bbb", 3);
    map2.insert("ccc", 4);
    
    map2.sort_by(|k1, _v1, k2, _v2| {
        k1.len().cmp(&k2.len()).then(k1.cmp(k2))
    });
    
    println!("\nSorted by length then key:");
    for (key, value) in &map2 {
        println!("  {} = {}", key, value);
    }
}

Swapping and Moving

use indexmap::IndexMap;
 
fn main() {
    let mut map = IndexMap::new();
    map.insert("apple", 1);
    map.insert("banana", 2);
    map.insert("cherry", 3);
    map.insert("date", 4);
    
    println!("Before swap: {:?}", map.keys().collect::<Vec<_>>());
    
    // Swap two elements by index
    map.swap_indices(0, 3);
    println!("After swap 0 and 3: {:?}", map.keys().collect::<Vec<_>>());
    
    // Move element to end
    map.move_index(1, 3);  // Move index 1 to position 3
    println!("After move 1 to 3: {:?}", map.keys().collect::<Vec<_>>());
}
 
// Reordering elements
fn reordering() {
    use indexmap::IndexMap;
    
    let mut map = IndexMap::new();
    map.insert("first", 1);
    map.insert("second", 2);
    map.insert("third", 3);
    map.insert("fourth", 4);
    
    // Move to front
    if let Some(idx) = map.get_index_of("third") {
        map.move_index(idx, 0);
    }
    println!("After moving third to front: {:?}", map.keys().collect::<Vec<_>>());
    
    // Move to back
    if let Some(idx) = map.get_index_of("first") {
        map.move_index(idx, map.len() - 1);
    }
    println!("After moving first to back: {:?}", map.keys().collect::<Vec<_>>());
}

IndexSet

use indexmap::IndexSet;
 
fn main() {
    let mut set = IndexSet::new();
    
    // Insert in order
    set.insert("apple");
    set.insert("banana");
    set.insert("cherry");
    
    // Iterate in insertion order
    for value in &set {
        println!("{}", value);
    }
    
    // Contains
    println!("Contains apple: {}", set.contains("apple"));
    
    // Length
    println!("Length: {}", set.len());
    
    // Get by index
    let item = set.get_index(1);
    println!("Index 1: {:?}", item);
    
    // Get index of
    let idx = set.get_index_of("banana");
    println!("Index of banana: {:?}", idx);
    
    // Remove
    set.remove("banana");
    println!("After remove: {:?}", set.iter().collect::<Vec<_>>());
    
    // Difference, intersection, etc.
    let set1: IndexSet<_> = [1, 2, 3, 4].into_iter().collect();
    let set2: IndexSet<_> = [3, 4, 5, 6].into_iter().collect();
    
    // Intersection
    let intersection: IndexSet<_> = set1.intersection(&set2).collect();
    println!("Intersection: {:?}", intersection);
    
    // Difference
    let diff: IndexSet<_> = set1.difference(&set2).collect();
    println!("Difference: {:?}", diff);
    
    // Union
    let union: IndexSet<_> = set1.union(&set2).collect();
    println!("Union: {:?}", union);
}
 
// IndexSet operations
fn set_operations() {
    use indexmap::IndexSet;
    
    let mut a: IndexSet<i32> = [1, 2, 3].into_iter().collect();
    let b: IndexSet<i32> = [3, 4, 5].into_iter().collect();
    
    // Insert returns (index, was_new)
    let (idx, was_new) = a.insert(4);
    println!("Inserted 4 at index {}, new: {}", idx, was_new);
    
    let (idx, was_new) = a.insert(4);  // Duplicate
    println!("Inserted 4 again at index {}, new: {}", idx, was_new);
    
    // Replace (insert and return old index if existed)
    let mut set = IndexSet::new();
    set.insert("apple");
    set.insert("banana");
    
    let replaced = set.replace("apple");
    println!("Replaced: {:?}", replaced);
}

Set Comparison

use indexmap::IndexSet;
 
fn main() {
    let a: IndexSet<_> = [1, 2, 3].into_iter().collect();
    let b: IndexSet<_> = [1, 2, 3].into_iter().collect();
    let c: IndexSet<_> = [1, 2, 3, 4].into_iter().collect();
    let d: IndexSet<_> = [3, 2, 1].into_iter().collect();
    
    // Equality (order matters!)
    println!("a == b: {}", a == b);  // true - same elements, same order
    println!("a == c: {}", a == c);  // false - different elements
    println!("a == d: {}", a == d);  // false - different order
    
    // Set equivalence (ignoring order)
    fn set_eq<T: std::hash::Hash + Eq>(a: &IndexSet<T>, b: &IndexSet<T>) -> bool {
        a.len() == b.len() && a.is_subset(b)
    }
    
    println!("a equiv d: {}", set_eq(&a, &d));  // true
    
    // Subset/superset
    let small: IndexSet<_> = [1, 2].into_iter().collect();
    let large: IndexSet<_> = [1, 2, 3, 4].into_iter().collect();
    
    println!("small is_subset of large: {}", small.is_subset(&large));
    println!("large is_superset of small: {}", large.is_superset(&small));
}

Capacity and Memory

use indexmap::IndexMap;
 
fn main() {
    let mut map: IndexMap<i32, i32> = IndexMap::new();
    
    println!("Initial capacity: {}", map.capacity());
    
    // Reserve capacity
    map.reserve(100);
    println!("After reserve: {}", map.capacity());
    
    // Shrink to fit
    for i in 0..10 {
        map.insert(i, i * 2);
    }
    println!("Len: {}, Capacity: {}", map.len(), map.capacity());
    
    map.shrink_to_fit();
    println!("After shrink_to_fit: {}", map.capacity());
    
    // Shrink to minimum capacity
    map.shrink_to(5);
    println!("After shrink_to(5): {}", map.capacity());
}

Serde Integration

# Cargo.toml
[dependencies]
indexmap = { version = "2", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
 
#[derive(Debug, Serialize, Deserialize)]
struct Config {
    name: String,
    settings: IndexMap<String, i32>,
}
 
fn main() {
    let mut config = Config {
        name: "test".to_string(),
        settings: IndexMap::new(),
    };
    
    config.settings.insert("first".to_string(), 1);
    config.settings.insert("second".to_string(), 2);
    config.settings.insert("third".to_string(), 3);
    
    // Serialize to JSON (preserves order!)
    let json = serde_json::to_string_pretty(&config).unwrap();
    println!("JSON:\n{}", json);
    
    // Deserialize (preserves order!)
    let parsed: Config = serde_json::from_str(&json).unwrap();
    println!("\nParsed: {:?}", parsed.settings.keys().collect::<Vec<_>>());
}
 
// JSON object with preserved order
fn json_ordering() {
    use indexmap::IndexMap;
    
    let mut obj: IndexMap<String, serde_json::Value> = IndexMap::new();
    obj.insert("z".to_string(), serde_json::json!(1));
    obj.insert("a".to_string(), serde_json::json!(2));
    obj.insert("m".to_string(), serde_json::json!(3));
    
    let json = serde_json::to_string(&obj).unwrap();
    println!("JSON (order preserved): {}", json);
    // {"z":1,"a":2,"m":3}
}

Converting Between Types

use indexmap::IndexMap;
use std::collections::HashMap;
 
fn main() {
    // From HashMap to IndexMap
    let std_map: HashMap<_, _> = [
        ("c", 3),
        ("a", 1),
        ("b", 2),
    ].into_iter().collect();
    
    let index_map: IndexMap<_, _> = std_map.into_iter().collect();
    println!("From HashMap: {:?}", index_map.keys().collect::<Vec<_>>());
    
    // From IndexMap to HashMap
    let std_map: HashMap<_, _> = index_map.into_iter().collect();
    println!("To HashMap (unordered iteration)");
    
    // From Vec of tuples
    let vec = vec![("x", 1), ("y", 2), ("z", 3)];
    let map: IndexMap<_, _> = vec.into_iter().collect();
    println!("From Vec: {:?}", map.keys().collect::<Vec<_>>());
    
    // To Vec
    let map = IndexMap::from([
        ("a", 1),
        ("b", 2),
        ("c", 3),
    ]);
    
    let keys: Vec<_> = map.keys().collect();
    let values: Vec<_> = map.values().collect();
    let entries: Vec<_> = map.into_iter().collect();
    
    println!("Keys: {:?}", keys);
    println!("Values: {:?}", values);
    println!("Entries: {:?}", entries);
}
 
// From arrays and slices
fn from_collections() {
    use indexmap::IndexMap;
    
    // From array
    let map = IndexMap::from([
        ("a", 1),
        ("b", 2),
    ]);
    
    // From parallel arrays
    let keys = ["a", "b", "c"];
    let values = [1, 2, 3];
    let map: IndexMap<_, _> = keys.into_iter().zip(values).collect();
    
    println!("Zipped: {:?}", map);
}

Real-World Example: Configuration

use indexmap::IndexMap;
 
#[derive(Debug, Clone)]
struct Config {
    name: String,
    options: IndexMap<String, String>,
}
 
impl Config {
    fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            options: IndexMap::new(),
        }
    }
    
    fn set(&mut self, key: &str, value: &str) -> &mut Self {
        self.options.insert(key.to_string(), value.to_string());
        self
    }
    
    fn get(&self, key: &str) -> Option<&String> {
        self.options.get(key)
    }
    
    fn remove(&mut self, key: &str) -> Option<String> {
        self.options.remove(key)
    }
    
    fn keys(&self) -> impl Iterator<Item = &String> {
        self.options.keys()
    }
    
    fn to_ordered_pairs(&self) -> Vec<(&String, &String)> {
        self.options.iter().collect()
    }
}
 
fn main() {
    let mut config = Config::new("my-app");
    
    // Set options in specific order
    config
        .set("host", "localhost")
        .set("port", "8080")
        .set("debug", "true")
        .set("timeout", "30");
    
    println!("Config: {}", config.name);
    for (key, value) in config.to_ordered_pairs() {
        println!("  {} = {}", key, value);
    }
    
    // Override preserves position
    config.set("host", "0.0.0.0");
    println!("\nAfter override:");
    for key in config.keys() {
        println!("  {}", key);
    }
}

Real-World Example: Ordered JSON

use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
 
#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    status: String,
    data: IndexMap<String, serde_json::Value>,
}
 
fn main() {
    let mut response = ApiResponse {
        status: "success".to_string(),
        data: IndexMap::new(),
    };
    
    // Add fields in specific order
    response.data.insert("id".to_string(), serde_json::json!(42));
    response.data.insert("name".to_string(), serde_json::json!("Alice"));
    response.data.insert("email".to_string(), serde_json::json!("alice@example.com"));
    response.data.insert("active".to_string(), serde_json::json!(true));
    
    // JSON output preserves field order
    let json = serde_json::to_string_pretty(&response).unwrap();
    println!("{}", json);
}
 
// Template with ordered placeholders
struct Template {
    placeholders: IndexMap<String, String>,
}
 
impl Template {
    fn new() -> Self {
        Self {
            placeholders: IndexMap::new(),
        }
    }
    
    fn add(&mut self, key: &str, default: &str) {
        self.placeholders.insert(key.to_string(), default.to_string());
    }
    
    fn render(&self, values: &IndexMap<String, String>) -> String {
        let mut result = String::new();
        for (key, default) in &self.placeholders {
            let value = values.get(key).unwrap_or(default);
            result.push_str(&format!("{}: {}\n", key, value));
        }
        result
    }
}
 
fn template_example() {
    let mut template = Template::new();
    template.add("greeting", "Hello");
    template.add("name", "World");
    template.add("punctuation", "!");
    
    let mut values = IndexMap::new();
    values.insert("name".to_string(), "Rust".to_string());
    
    println!("{}", template.render(&values));
}

Real-World Example: LRU-like Cache

use indexmap::IndexMap;
 
struct LruCache<K, V> {
    capacity: usize,
    cache: IndexMap<K, V>,
}
 
impl<K: std::hash::Hash + Eq + Clone, V: Clone> LruCache<K, V> {
    fn new(capacity: usize) -> Self {
        Self {
            capacity,
            cache: IndexMap::new(),
        }
    }
    
    fn get(&mut self, key: &K) -> Option<&V> {
        // Move to end (most recently used)
        if let Some(idx) = self.cache.get_index_of(key) {
            let (k, v) = self.cache.swap_remove_index(idx).unwrap();
            self.cache.insert(k, v);
            return self.cache.get(key);
        }
        None
    }
    
    fn put(&mut self, key: K, value: V) {
        // Remove if exists
        if let Some(_) = self.cache.swap_remove(&key) {
            // Already removed
        }
        
        // Evict oldest if at capacity
        if self.cache.len() >= self.capacity {
            self.cache.shift_remove_index(0);  // Remove first (oldest)
        }
        
        self.cache.insert(key, value);
    }
    
    fn len(&self) -> usize {
        self.cache.len()
    }
    
    fn is_empty(&self) -> bool {
        self.cache.is_empty()
    }
}
 
fn main() {
    let mut cache = LruCache::new(3);
    
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    
    println!("Cache size: {}", cache.len());
    
    // Access "a" - moves to most recent
    cache.get(&"a".to_string());
    
    // Add new item - evicts "b" (oldest after access)
    cache.put("d", 4);
    
    println!("After adding d, has a: {}", cache.get(&"a".to_string()).is_some());
    println!("Has b: {}", cache.get(&"b".to_string()).is_some());  // false - evicted
    println!("Has c: {}", cache.get(&"c".to_string()).is_some());  // true
    println!("Has d: {}", cache.get(&"d".string()).is_some());  // true
}

Summary

  • IndexMap preserves insertion order, unlike HashMap
  • IndexSet is an ordered version of HashSet
  • Use .get_index(n) to access by position
  • Use .get_index_of(key) to find position of a key
  • Use .swap_remove() for O(1) removal (changes order)
  • Use .shift_remove() for O(n) removal (preserves order)
  • Use .sort_keys() to sort by key in place
  • Use .sort_by() for custom sorting
  • Use .swap_indices(i, j) to swap elements by position
  • Use .move_index(from, to) to move an element
  • Entry API works like HashMap with or_insert, and_modify, etc.
  • Serde feature preserves JSON object key order
  • Ideal for configuration, JSON, templates, and ordered data
  • IndexSet supports set operations: intersection, union, difference
  • Slightly higher memory overhead than HashMap for order tracking