Loading page…
Rust walkthroughs
Loading page…
The indexmap crate provides hash-based data structures that maintain insertion order. Unlike the standard HashMap and HashSet, which have arbitrary iteration order, IndexMap and IndexSet iterate in the order elements were inserted. This makes them ideal for JSON serialization, configuration parsing, ordered caches, and any scenario where deterministic iteration order matters while maintaining O(1) lookup performance.
Key concepts:
get_index()# Cargo.toml
[dependencies]
indexmap = "2.0"use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("c", 3);
map.insert("a", 1);
map.insert("b", 2);
// Iteration order matches insertion order
for (key, value) in &map {
println!("{}: {}", key, value); // c, a, b
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
// Insert in specific order
map.insert("apple", 5);
map.insert("banana", 3);
map.insert("cherry", 8);
// Lookup by key (O(1))
println!("apple count: {:?}", map.get("apple"));
// Iteration preserves insertion order
println!("\nIn insertion order:");
for (key, value) in &map {
println!(" {} => {}", key, value);
}
// Keys(), values() also preserve order
println!("\nKeys: {:?}", map.keys().collect::<Vec<_>>());
println!("Values: {:?}", map.values().collect::<Vec<_>>());
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("first", 1);
map.insert("second", 2);
map.insert("third", 3);
// Access by index (O(1))
println!("Index 0: {:?}", map.get_index(0));
println!("Index 1: {:?}", map.get_index(1));
println!("Index 2: {:?}", map.get_index(2));
// Get mutable reference by index
if let Some((key, value)) = map.get_index_mut(1) {
println!("\nModifying {}", key);
*value *= 10;
}
// Get key-value pair by index
for i in 0..map.len() {
if let Some((k, v)) = map.get_index(i) {
println!("Position {}: {} = {}", i, k, v);
}
}
}use std::collections::HashMap;
use indexmap::IndexMap;
fn main() {
// Standard HashMap - unpredictable order
let mut std_map = HashMap::new();
std_map.insert("c", 3);
std_map.insert("a", 1);
std_map.insert("b", 2);
println!("HashMap iteration order:");
for (k, v) in &std_map {
println!(" {} => {}", k, v);
}
// IndexMap - insertion order guaranteed
let mut index_map = IndexMap::new();
index_map.insert("c", 3);
index_map.insert("a", 1);
index_map.insert("b", 2);
println!("\nIndexMap iteration order:");
for (k, v) in &index_map {
println!(" {} => {}", k, v);
}
}use indexmap::IndexSet;
fn main() {
let mut set = IndexSet::new();
// Insert in specific order
set.insert("red");
set.insert("green");
set.insert("blue");
set.insert("red"); // Duplicate - ignored
// Check membership
println!("Contains red: {}", set.contains("red"));
println!("Contains yellow: {}", set.contains("yellow"));
// Iteration preserves insertion order
println!("\nIn insertion order:");
for value in &set {
println!(" {}", value);
}
// Access by index
println!("\nBy index:");
for i in 0..set.len() {
println!(" Index {}: {:?}", i, set.get_index(i));
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
// insert() returns old value if key exists
map.insert("a", 1);
let old = map.insert("a", 2);
println!("Old value for 'a': {:?}", old);
// entry() API (similar to HashMap)
map.entry("b").or_insert(10);
map.entry("b").and_modify(|v| *v += 1).or_insert(1);
// Update without changing position
if let Some(value) = map.get_mut("a") {
*value = 100;
}
for (k, v) in &map {
println!("{} => {}", k, v);
}
}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!("Before removal:");
for (k, v) in &map {
println!(" {} => {}", k, v);
}
// Remove by key (shifts subsequent elements)
map.remove("b");
println!("\nAfter remove('b'):");
for (k, v) in &map {
println!(" {} => {}", k, v);
}
// Remove by index
map.remove_index(0); // Removes 'a'
println!("\nAfter remove_index(0):");
for (k, v) in &map {
println!(" {} => {}", k, v);
}
// Swap remove (O(1), but changes order)
let mut map2 = IndexMap::new();
map2.insert("a", 1);
map2.insert("b", 2);
map2.insert("c", 3);
map2.swap_remove("b"); // Swaps last element to 'b's position
println!("\nAfter swap_remove('b'):");
for (k, v) in &map2 {
println!(" {} => {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("c", 3);
// Insert at specific position
map.insert_before("b", "c", 2); // Insert "b" before "c"
// Alternatively, use insert_sorted for sorted insertion
let mut sorted_map = IndexMap::new();
sorted_map.insert("c", 3);
sorted_map.insert("a", 1);
sorted_map.insert("b", 2);
// Re-sort by key
sorted_map.sort_keys();
println!("Sorted by key:");
for (k, v) in &sorted_map {
println!(" {} => {}", k, v);
}
}// Cargo.toml:
// indexmap = { version = "2.0", features = ["serde"] }
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("z", 1);
map.insert("a", 2);
map.insert("m", 3);
// Serialize to JSON (order preserved)
let json = serde_json::to_string(&map).unwrap();
println!("JSON: {}", json);
// Output: {"z":1,"a":2,"m":3}
// Deserialize back (order preserved)
let parsed: IndexMap<String, i32> = serde_json::from_str(&json).unwrap();
println!("\nDeserialized order:");
for (k, v) in &parsed {
println!(" {} => {}", k, v);
}
}// Cargo.toml:
// indexmap = { version = "2.0", features = ["serde"] }
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Config {
// Preserve field order for readable config files
#[serde(flatten)]
settings: IndexMap<String, String>,
}
fn main() {
let json = r#"{
"database_host": "localhost",
"database_port": "5432",
"api_key": "secret",
"timeout": "30"
}"#;
let config: Config = serde_json::from_str(json).unwrap();
println!("Settings in file order:");
for (key, value) in &config.settings {
println!(" {} = {}", key, value);
}
// Re-serialize preserves order
let output = serde_json::to_string_pretty(&config).unwrap();
println!("\nRe-serialized:\n{}", output);
}use indexmap::IndexMap;
struct OrderedDictionary {
words: IndexMap<String, Vec<String>>,
}
impl OrderedDictionary {
fn new() -> Self {
Self { words: IndexMap::new() }
}
fn add_word(&mut self, word: &str, definitions: Vec<String>) {
self.words.insert(word.to_string(), definitions);
}
fn lookup(&self, word: &str) -> Option<&Vec<String>> {
self.words.get(word)
}
fn word_at(&self, index: usize) -> Option<(&String, &Vec<String>)> {
self.words.get_index(index)
}
fn words(&self) -> impl Iterator<Item = &String> {
self.words.keys()
}
}
fn main() {
let mut dict = OrderedDictionary::new();
// Add words in specific order
dict.add_word("apple", vec!["A fruit".into(), "A tech company".into()]);
dict.add_word("banana", vec!["A yellow fruit".into()]);
dict.add_word("cherry", vec!["A small red fruit".into()]);
println!("Words in order:");
for word in dict.words() {
println!(" - {}", word);
}
println!("\nFirst word: {:?}", dict.word_at(0));
println!("Definition of 'banana': {:?}", dict.lookup("banana"));
}use indexmap::IndexMap;
struct LruCache<K, V> {
cache: IndexMap<K, V>,
capacity: usize,
}
impl<K: std::hash::Hash + Eq + Clone, V> LruCache<K, V> {
fn new(capacity: usize) -> Self {
Self {
cache: IndexMap::with_capacity(capacity),
capacity,
}
}
fn get(&mut self, key: &K) -> Option<&V> {
// Move to end (most recently used)
if self.cache.contains_key(key) {
let (_, value) = self.cache.remove_entry(key).unwrap();
self.cache.insert(key.clone(), value);
}
self.cache.get(key)
}
fn put(&mut self, key: K, value: V) {
// Remove if exists
self.cache.remove(&key);
// Evict oldest if at capacity
if self.cache.len() >= self.capacity {
self.cache.remove_index(0);
}
self.cache.insert(key, value);
}
fn len(&self) -> usize {
self.cache.len()
}
}
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 it to most recently used
cache.get(&"a".to_string());
// Add new item - evicts 'b' (oldest after 'a' access)
cache.put("d", 4);
println!("\nCache contents (oldest to newest):");
// Note: This accesses internal cache directly for demo
for (k, v) in &cache.cache {
println!(" {} => {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("first", 1);
map.insert("middle", 2);
map.insert("last", 3);
// Get first and last elements
println!("First: {:?}", map.first());
println!("Last: {:?}", map.last());
// Mutable access to last
if let Some((key, value)) = map.last_mut() {
println!("\nModifying last key: {}", key);
*value = 100;
}
// Pop last element
let popped = map.pop();
println!("\nPopped: {:?}", popped);
println!("Remaining: {:?}", map.iter().collect::<Vec<_>>());
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("apple", 5);
map.insert("banana", 3);
map.insert("cherry", 8);
// Get the index of a key
if let Some(index) = map.get_index_of("banana") {
println!("'banana' is at index {}", index);
}
// Check if contains and get index
let key_to_find = "cherry";
match map.get_full(key_to_find) {
Some((index, key, value)) => {
println!("Found '{}' at index {} with value {}", key, index, value);
}
None => println!("Key not found"),
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Iterate with indices
for (index, (key, value)) in map.iter().enumerate() {
println!("Index {}: {} = {}", index, key, value);
}
// Mutable iteration
for (_, value) in map.iter_mut() {
*value *= 2;
}
println!("\nAfter doubling:");
for (k, v) in &map {
println!(" {} => {}", k, v);
}
// Drain all elements
let drained: Vec<_> = map.drain(..).collect();
println!("\nDrained: {:?}", drained);
println!("Map is now empty: {}", map.is_empty());
}use indexmap::IndexSet;
fn main() {
let mut set1 = IndexSet::new();
set1.insert("a");
set1.insert("b");
set1.insert("c");
let mut set2 = IndexSet::new();
set2.insert("b");
set2.insert("c");
set2.insert("d");
// Intersection
let intersection: IndexSet<_> = set1.intersection(&set2).cloned().collect();
println!("Intersection: {:?}", intersection);
// Union
let union: IndexSet<_> = set1.union(&set2).cloned().collect();
println!("Union: {:?}", union);
// Difference
let diff: IndexSet<_> = set1.difference(&set2).cloned().collect();
println!("Difference (set1 - set2): {:?}", diff);
// Symmetric difference
let sym_diff: IndexSet<_> = set1.symmetric_difference(&set2).cloned().collect();
println!("Symmetric difference: {:?}", sym_diff);
}// Cargo.toml:
// indexmap = { version = "2.0", features = ["serde"] }
use indexmap::IndexMap;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct ApiResponse {
#[serde(flatten)]
data: IndexMap<String, serde_json::Value>,
}
fn main() {
let json = r#"{
"user_3": {"name": "Charlie"},
"user_1": {"name": "Alice"},
"user_2": {"name": "Bob"}
}"#;
let response: ApiResponse = serde_json::from_str(json).unwrap();
println!("Users in original order:");
for (key, value) in &response.data {
println!(" {}: {:?}", key, value);
}
}use indexmap::IndexMap;
fn main() {
// Pre-allocate capacity
let mut map = IndexMap::with_capacity(100);
println!("Capacity: {}, Len: {}", map.capacity(), map.len());
// Add items
for i in 0..50 {
map.insert(format!("key_{}", i), i);
}
println!("After inserts - Capacity: {}, Len: {}", map.capacity(), map.len());
// Shrink to fit
map.shrink_to_fit();
println!("After shrink - Capacity: {}, Len: {}", map.capacity(), map.len());
}use indexmap::IndexMap;
use std::hash::{BuildHasherDefault, Hasher};
// Simple custom hasher for demonstration
#[derive(Default)]
struct SimpleHasher(u64);
impl Hasher for SimpleHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
self.0 = self.0.wrapping_add(*byte as u64);
}
}
}
fn main() {
let mut map: IndexMap<String, i32, BuildHasherDefault<SimpleHasher>> =
IndexMap::default();
map.insert("hello".to_string(), 1);
map.insert("world".to_string(), 2);
for (k, v) in &map {
println!("{}: {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map1 = IndexMap::new();
map1.insert("a", 1);
map1.insert("b", 2);
let mut map2 = IndexMap::new();
map2.insert("a", 1);
map2.insert("b", 2);
let mut map3 = IndexMap::new();
map3.insert("b", 2);
map3.insert("a", 1);
// Equal: same keys, values, and order
println!("map1 == map2: {}", map1 == map2);
// Not equal: different order
println!("map1 == map3: {}", map1 == map3);
}IndexMap preserves insertion order unlike HashMapIndexSet preserves insertion order unlike HashSetget_index() and get_index_mut()get_index_of() and get_full()remove() for order-preserving removal, swap_remove() for O(1)insert_before() and insert_sorted()sort_keys() and sort_by()first(), last(), first_mut(), last_mut()serde feature for JSON serialization that preserves order