Loading page…
Rust walkthroughs
Loading page…
The indexmap crate provides hash maps and sets that preserve insertion order. Unlike HashMap and HashSet which have unpredictable iteration order, IndexMap and IndexSet iterate in the order items were inserted. They also support efficient index-based access, making them useful when you need both key-based lookup and ordered traversal or position-based operations.
Key concepts:
HashMap/HashSet# Cargo.toml
[dependencies]
indexmap = "2.0"use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
// Insert in specific order
map.insert("apple", 1);
map.insert("banana", 2);
map.insert("cherry", 3);
// Iteration preserves insertion order
for (key, value) in &map {
println!("{}: {}", key, value);
}
// Access by index
println!("First item: {:?}", map.get_index(0));
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<String, i32> = IndexMap::new();
// Insert values
map.insert("first".to_string(), 1);
map.insert("second".to_string(), 2);
map.insert("third"._string(), 3);
// Get value by key
if let Some(value) = map.get("second") {
println!("Value for 'second': {}", value);
}
// Check if key exists
println!("Contains 'first': {}", map.contains_key("first"));
println!("Contains 'fourth': {}", map.contains_key("fourth"));
// Update value
map.insert("second".to_string(), 20);
println!("Updated 'second': {:?}", map.get("second"));
// Remove by key
if let Some((k, v)) = map.shift_remove("third") {
println!("Removed: {} = {}", k, v);
}
// Current size
println!("Size: {}", map.len());
println!("Is empty: {}", map.is_empty());
// Clear all
map.clear();
println!("After clear, is empty: {}", map.is_empty());
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Get by index
println!("Index 0: {:?}", map.get_index(0));
println!("Index 2: {:?}", map.get_index(2));
println!("Index 10: {:?}", map.get_index(10)); // None
// Get key at index
println!("Key at index 1: {:?}", map.get_index(1).map(|(k, _)| k));
// Get mutable reference by index
if let Some((key, value)) = map.get_index_mut(1) {
println!("Before: {} = {}", key, value);
*value = 20;
}
// Get full entry by index
let entry = map.get_index_entry(2);
println!("Entry at index 2: {:?}", entry);
// Find index of a key
println!("Index of 'b': {:?}", map.get_index_of("b"));
println!("Index of 'z': {:?}", map.get_index_of("z")); // None
// Iterate with indices
for (idx, (key, value)) in map.iter().enumerate() {
println!("Index {}: {} = {}", idx, key, value);
}
}use indexmap::IndexMap;
use std::collections::HashMap;
fn main() {
// HashMap - unpredictable order
let mut hash_map: HashMap<&str, i32> = HashMap::new();
hash_map.insert("cherry", 3);
hash_map.insert("apple", 1);
hash_map.insert("banana", 2);
hash_map.insert("date", 4);
println!("HashMap iteration (unpredictable):";
for (k, v) in &hash_map {
print!("{}={}, ", k, v);
}
println!();
// IndexMap - preserves insertion order
let mut index_map: IndexMap<&str, i32> = IndexMap::new();
index_map.insert("cherry", 3);
index_map.insert("apple", 1);
index_map.insert("banana", 2);
index_map.insert("date", 4);
println!("\nIndexMap iteration (insertion order):";
for (k, v) in &index_map {
print!("{}={}, ", k, v);
}
println!();
// This order is guaranteed and consistent
let keys: Vec<_> = index_map.keys().collect();
println!("\nKeys in order: {:?}", keys);
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
map.insert("e", 5);
println!("Initial: {:?}", map.iter().collect::<Vec<_>>());
// shift_remove: preserves order of remaining elements (O(n))
map.shift_remove("c");
println!("After shift_remove('c'): {:?}", map.iter().collect::<Vec<_>>());
// swap_remove: swaps with last element, O(1) but changes order
map.insert("c", 3); // Add back
map.swap_remove("b");
println!("After swap_remove('b'): {:?}", map.iter().collect::<Vec<_>>());
// Remove by index
map.shift_remove_index(1);
println!("After shift_remove_index(1): {:?}", map.iter().collect::<Vec<_>>());
// pop: remove the last element
if let Some((k, v)) = map.pop() {
println!("Popped: {} = {}", k, v);
}
println!("After pop: {:?}", map.iter().collect::<Vec<_>>());
}use indexmap::IndexSet;
fn main() {
let mut set: IndexSet<&str> = IndexSet::new();
// Insert values
set.insert("apple");
set.insert("banana");
set.insert("cherry");
set.insert("banana"); // Duplicate, won't be added
println!("Set contents (order preserved):");
for item in &set {
println!(" {}", item);
}
// Check membership
println!("\nContains 'banana': {}", set.contains("banana"));
println!("Contains 'date': {}", set.contains("date"));
// Get by index
println!("\nIndex 0: {:?}", set.get_index(0));
println!("Index 2: {:?}", set.get_index(2));
// Get index of value
println!("Index of 'cherry': {:?}", set.get_index_of("cherry"));
// Remove by value
set.shift_remove("banana");
println!("\nAfter removing 'banana':");
for (i, item) in set.iter().enumerate() {
println!(" {}: {}", i, item);
}
// Remove by index
set.shift_remove_index(0);
println!("\nAfter removing index 0:");
for item in &set {
println!(" {}", item);
}
}use indexmap::IndexSet;
fn main() {
let mut a: IndexSet<i32> = IndexSet::new();
a.insert(1);
a.insert(2);
a.insert(3);
let mut b: IndexSet<i32> = IndexSet::new();
b.insert(2);
b.insert(3);
b.insert(4);
// Union (preserves order from a, then b)
let union: IndexSet<i32> = a.union(&b).cloned().collect();
println!("Union: {:?}", union.iter().collect::<Vec<_>>());
// Intersection (preserves order from a)
let intersection: IndexSet<i32> = a.intersection(&b).cloned().collect();
println!("Intersection: {:?}", intersection.iter().collect::<Vec<_>>());
// Difference (preserves order from a)
let difference: IndexSet<i32> = a.difference(&b).cloned().collect();
println!("Difference (a - b): {:?}", difference.iter().collect::<Vec<_>>());
// Symmetric difference (preserves order)
let sym_diff: IndexSet<i32> = a.symmetric_difference(&b).cloned().collect();
println!("Symmetric difference: {:?}", sym_diff.iter().collect::<Vec<_>>());
// Subset/superset checks
let mut c: IndexSet<i32> = IndexSet::new();
c.insert(1);
c.insert(2);
println!("\nIs {1,2} subset of {1,2,3}: {}", c.is_subset(&a));
println!("Is {1,2,3} superset of {1,2}: {}", a.is_superset(&c));
println!("Is {1,2} disjoint from {3,4}: {}", c.is_disjoint(&b));
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
// entry API similar to HashMap
map.entry("apple").or_insert(1);
map.entry("banana").or_insert(2);
// Won't overwrite existing
map.entry("apple").or_insert(100);
println!("apple: {:?}", map.get("apple")); // Still 1
// or_insert_with for lazy evaluation
map.entry("cherry").or_insert_with(|| {
println!("Computing value for cherry");
3
});
// and_modify to update existing
map.entry("apple").and_modify(|v| *v *= 10);
println!("apple after modify: {:?}", map.get("apple"));
// Combine or_insert_with and and_modify
map.entry("banana")
.and_modify(|v| *v += 1)
.or_insert(1);
println!("\nFinal map:");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Iterate over key-value pairs
println!("Key-value pairs:");
for (key, value) in map.iter() {
println!(" {} = {}", key, value);
}
// Iterate over keys
println!("\nKeys:");
for key in map.keys() {
println!(" {}", key);
}
// Iterate over values
println!("\nValues:");
for value in map.values() {
println!(" {}", value);
}
// Mutable iteration
println!("\nValues (mutable, doubled):");
for value in map.values_mut() {
*value *= 2;
}
for (k, v) in &map {
println!(" {} = {}", k, v);
}
// Into iteration (consumes map)
let map2: IndexMap<&str, i32> = [("x", 10), ("y", 20)].into_iter().collect();
println!("\nFrom iterator:");
for (k, v) in map2 {
println!(" {} = {}", k, v);
}
}use indexmap::{IndexMap, IndexSet};
fn main() {
// From array
let map: IndexMap<&str, i32> = [
("apple", 1),
("banana", 2),
("cherry", 3),
].into_iter().collect();
println!("From array:");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
// From vec of tuples
let vec = vec![(1, "one"), (2, "two"), (3, "three")];
let map2: IndexMap<i32, &str> = vec.into_iter().collect();
println!("\nFrom vec:");
for (k, v) in &map2 {
println!(" {} = {}", k, v);
}
// Set from iterator
let set: IndexSet<_> = ["x", "y", "z", "y"].iter().cloned().collect();
println!("\nSet from iterator: {:?}", set.iter().collect::<Vec<_>>());
// With capacity
let mut map_with_cap: IndexMap<i32, String> = IndexMap::with_capacity(100);
println!("Capacity: {}", map_with_cap.capacity());
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
map.insert("cherry", 3);
map.insert("apple", 1);
map.insert("banana", 2);
map.insert("date", 4);
println!("Original order:");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
// Sort by key
map.sort_keys();
println!("\nAfter sort_keys():");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
// Sort by values (using sort_by)
map.sort_by(|k1, v1, k2, v2| v1.cmp(v2));
println!("\nAfter sort_by value:");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
// Reverse sort
map.sort_by(|k1, v1, k2, v2| v2.cmp(v1));
println!("\nAfter reverse sort by value:");
for (k, v) in &map {
println!(" {} = {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map: IndexMap<&str, i32> = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
println!("Before swap: {:?}", map.keys().collect::<Vec<_>>());
// Swap two entries by index
map.swap_indices(0, 3);
println!("After swap(0, 3): {:?}", map.keys().collect::<Vec<_>>());
// Move entry to end
let index = map.get_index_of("b").unwrap();
map.move_index(index, map.len() - 1);
println!("After moving 'b' to end: {:?}", map.keys().collect::<Vec<_>>());
// Reverse the map
let reversed: IndexMap<&str, i32> = map.into_iter().rev().collect();
println!("Reversed: {:?}", reversed.keys().collect::<Vec<_>>());
}use indexmap::IndexMap;
fn main() {
// Create with capacity
let mut map: IndexMap<i32, &str> = IndexMap::with_capacity(10);
println!("Initial capacity: {}", map.capacity());
// Add items
for i in 0..5 {
map.insert(i, &format!("value{}", i));
}
println!("After 5 inserts - len: {}, capacity: {}", map.len(), map.capacity());
// Reserve additional capacity
map.reserve(100);
println!("After reserve(100) - capacity: {}", map.capacity());
// Shrink to fit
map.shrink_to_fit();
println!("After shrink_to_fit - capacity: {}", map.capacity());
// Shrink to minimum capacity
map.shrink_to(10);
println!("After shrink_to(10) - capacity: {}", map.capacity());
}use indexmap::IndexMap;
#[derive(Debug, Clone)]
struct Config {
values: IndexMap<String, String>,
}
impl Config {
fn new() -> Self {
Self {
values: IndexMap::new(),
}
}
fn set(&mut self, key: &str, value: &str) {
self.values.insert(key.to_string(), value.to_string());
}
fn get(&self, key: &str) -> Option<&String> {
self.values.get(key)
}
fn remove(&mut self, key: &str) -> Option<String> {
self.values.shift_remove(key)
}
// Get value at specific position
fn get_at(&self, index: usize) -> Option<(&String, &String)> {
self.values.get_index(index)
}
// Find the position of a key
fn position(&self, key: &str) -> Option<usize> {
self.values.get_index_of(key)
}
// Render as ordered list
fn to_ordered_list(&self) -> Vec<String> {
self.values
.iter()
.map(|(k, v)| format!("{} = {}", k, v))
.collect()
}
}
fn main() {
let mut config = Config::new();
// Set values in specific order
config.set("host", "localhost");
config.set("port", "8080");
config.set("database", "myapp");
config.set("user", "admin");
println!("Configuration (in order):");
for line in config.to_ordered_list() {
println!(" {}", line);
}
// Access by position
println!("\nFirst config: {:?}", config.get_at(0));
// Find position
println!("Position of 'database': {:?}", config.position("database"));
// Update preserves order
config.set("port", "443");
println!("\nAfter updating port:");
for (k, v) in &config.values {
println!(" {} = {}", k, v);
}
}use indexmap::IndexMap;
#[derive(Debug)]
struct Headers {
headers: IndexMap<String, String>,
}
impl Headers {
fn new() -> Self {
Self {
headers: IndexMap::new(),
}
}
fn insert(&mut self, name: &str, value: &str) {
// Headers are case-insensitive, so store lowercase
self.headers.insert(name.to_lowercase(), value.to_string());
}
fn get(&self, name: &str) -> Option<&String> {
self.headers.get(&name.to_lowercase())
}
fn remove(&mut self, name: &str) -> Option<String> {
self.headers.shift_remove(&name.to_lowercase())
}
// Headers need to be sent in the same order they were added
fn to_http_string(&self) -> String {
self.headers
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.collect::<Vec<_>>()
.join("\r\n")
}
}
fn main() {
let mut headers = Headers::new();
// Standard headers in typical order
headers.insert("Host", "example.com");
headers.insert("User-Agent", "MyApp/1.0");
headers.insert("Accept", "application/json");
headers.insert("Authorization", "Bearer token123");
// Order is preserved
println!("HTTP Headers:\n{}\n", headers.to_http_string());
// Access headers
println!("Host: {:?}", headers.get("Host"));
println!("Authorization: {:?}", headers.get("authorization")); // case-insensitive
// Remove header
headers.remove("Authorization");
println!("\nAfter removing Authorization:");
println!("{}", headers.to_http_string());
}use indexmap::IndexMap;
#[derive(Debug, Clone)]
struct MenuItem {
name: String,
price: f64,
description: String,
}
struct Menu {
items: IndexMap<String, MenuItem>,
}
impl Menu {
fn new() -> Self {
Self {
items: IndexMap::new(),
}
}
fn add(&mut self, id: &str, name: &str, price: f64, description: &str) {
self.items.insert(id.to_string(), MenuItem {
name: name.to_string(),
price,
description: description.to_string(),
});
}
fn get(&self, id: &str) -> Option<&MenuItem> {
self.items.get(id)
}
fn get_by_position(&self, pos: usize) -> Option<(&str, &MenuItem)> {
self.items.get_index(pos).map(|(k, v)| (k.as_str(), v))
}
fn position(&self, id: &str) -> Option<usize> {
self.items.get_index_of(id)
}
fn display(&self) {
println!("Menu:");
for (idx, (id, item)) in self.items.iter().enumerate() {
println!(" {}. {} - ${:.2}", idx + 1, item.name, item.price);
println!(" ID: {}", id);
println!(" {}", item.description);
}
}
fn display_numbered(&self) {
println!("\nQuick menu (by number):");
for (idx, (_, item)) in self.items.iter().enumerate() {
println!(" {}. {} - ${:.2}", idx + 1, item.name, item.price);
}
}
}
fn main() {
let mut menu = Menu::new();
// Add items in display order
menu.add("burger", "Classic Burger", 12.99, "Beef patty with fresh vegetables");
menu.add("fries", "French Fries", 4.99, "Crispy golden fries");
menu.add("shake", "Milkshake", 6.99, "Choose vanilla, chocolate, or strawberry");
menu.add("soda", "Soft Drink", 2.99, "Various flavors available");
menu.display();
menu.display_numbered();
// Order by number
println!("\nOrdering item #2:");
if let Some((id, item)) = menu.get_by_position(1) {
println!(" You ordered: {} for ${:.2}", item.name, item.price);
}
// Look up by ID
println!("\nDetails for 'burger':");
if let Some(item) = menu.get("burger") {
println!(" {} - ${:.2}", item.name, item.price);
}
}use std::collections::HashMap;
use indexmap::IndexMap;
fn main() {
let data = vec![
("first", 1),
("second", 2),
("third", 3),
("fourth", 4),
];
// HashMap
let mut hashmap: HashMap<&str, i32> = HashMap::new();
for (k, v) in &data {
hashmap.insert(*k, *v);
}
// IndexMap
let mut indexmap: IndexMap<&str, i32> = IndexMap::new();
for (k, v) in &data {
indexmap.insert(*k, *v);
}
println!("HashMap keys (unordered): {:?}", hashmap.keys().collect::<Vec<_>>());
println!("IndexMap keys (ordered): {:?}", indexmap.keys().collect::<Vec<_>>());
// IndexMap supports index access
println!("\nIndexMap[0]: {:?}", indexmap.get_index(0));
println!("IndexMap index of 'third': {:?}", indexmap.get_index_of("third"));
// HashMap doesn't have these methods
// hashmap.get_index(0); // Error: no such method
}use indexmap::IndexMap;
use std::time::Instant;
fn main() {
// IndexMap has slightly higher memory overhead than HashMap
// due to maintaining order indices
let n = 100_000;
// Insert performance
let mut map = IndexMap::with_capacity(n);
let start = Instant::now();
for i in 0..n {
map.insert(i, i * 2);
}
let insert_time = start.elapsed();
println!("Insert {} items: {:?}", n, insert_time);
// Lookup performance (similar to HashMap)
let start = Instant::now();
let mut sum = 0;
for i in 0..n {
if let Some(v) = map.get(&i) {
sum += v;
}
}
let lookup_time = start.elapsed();
println!("Lookup {} items: {:?}", n, lookup_time);
// shift_remove is O(n), swap_remove is O(1)
let mut map2 = map.clone();
let start = Instant::now();
for i in 0..1000 {
map.shift_remove(&i);
}
let shift_time = start.elapsed();
println!("shift_remove 1000 items: {:?}", shift_time);
let start = Instant::now();
for i in 1000..2000 {
map2.swap_remove(&i);
}
let swap_time = start.elapsed();
println!("swap_remove 1000 items: {:?}", swap_time);
println!("\nTip: Use swap_remove when order doesn't matter");
println!("Tip: Use shift_remove when you need to preserve order");
}IndexMap and IndexSet preserve insertion order, unlike HashMap and HashSetget_index(i) and get_index_mut(i) for position-based accessget_index_of(key) to find the position of a keyshift_remove preserves order (O(n)), swap_remove is faster but changes order (O(1))sort_keys() sorts by key, sort_by() allows custom sortingHashMap: entry(key).or_insert(value)union, intersection, difference, symmetric_differenceHashMap due to order tracking