Loading page…
Rust walkthroughs
Loading page…
DashMap is a concurrent hash map that provides thread-safe access without requiring external locking. It uses sharding—dividing data into multiple segments each with its own lock—allowing concurrent operations on different keys with minimal contention. DashMap offers a HashMap-like API while being safe to share across threads.
Key features:
std::collections::HashMapDashMap is ideal for caches, registries, and shared state in concurrent applications.
# Cargo.toml
[dependencies]
dashmap = "5"use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
fn main() {
// Create a DashMap (internally sharded)
let map: DashMap<String, u32> = DashMap::new();
// Basic operations work like HashMap
map.insert("apple".to_string(), 1);
map.insert("banana".to_string(), 2);
// Read access
if let Some(entry) = map.get("apple") {
println!("apple: {}", *entry);
}
// Update existing value
if let Some(mut entry) = map.get_mut("banana") {
*entry += 10;
}
// Remove
map.remove("apple");
// Check existence
println!("Contains banana: {}", map.contains_key("banana"));
// Iterate
for entry in map.iter() {
println!("{}: {}", entry.key(), entry.value());
}
println!("Length: {}", map.len());
}use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
fn main() {
// Wrap in Arc for sharing across threads
let map = Arc::new(DashMap::<String, i32>::new());
let mut handles = vec![];
// Spawn multiple writer threads
for i in 0..10 {
let map_clone = Arc::clone(&map);
let handle = thread::spawn(move || {
for j in 0..100 {
let key = format!("thread-{}-key-{}", i, j);
map_clone.insert(key, i * 1000 + j);
}
});
handles.push(handle);
}
// Spawn reader threads concurrently
for _ in 0..5 {
let map_clone = Arc::clone(&map);
let handle = thread::spawn(move || {
for i in 0..100 {
let key = format!("thread-0-key-{}", i);
if let Some(value) = map_clone.get(&key) {
println!("Read: {} = {}", key, *value);
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Total entries: {}", map.len());
}use dashmap::DashMap;
fn main() {
let map: DashMap<String, u32> = DashMap::new();
// or_insert: insert if missing
map.entry("counter".to_string()).or_insert(0);
// or_insert_with: lazy insertion
map.entry("lazy".to_string()).or_insert_with(|| {
println!("Computing value...");
42
});
// and_modify: update existing value
map.entry("counter".to_string())
.and_modify(|v| *v += 1);
// Combined: update if exists, insert if not
map.entry("counter".to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
println!("Counter: {}", map.get("counter").unwrap());
// Entry key access
for entry in map.iter() {
println!("{}: {}", entry.key(), entry.value());
}
}use dashmap::DashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::thread;
#[derive(Clone)]
struct CacheEntry {
value: String,
expires_at: Instant,
}
struct Cache {
data: DashMap<String, CacheEntry>,
}
impl Cache {
fn new() -> Self {
Cache {
data: DashMap::new(),
}
}
fn get(&self, key: &str) -> Option<String> {
self.data.get(key).and_then(|entry| {
if entry.expires_at > Instant::now() {
Some(entry.value.clone())
} else {
None // Expired
}
})
}
fn set(&self, key: String, value: String, ttl: Duration) {
let entry = CacheEntry {
value,
expires_at: Instant::now() + ttl,
};
self.data.insert(key, entry);
}
fn remove(&self, key: &str) {
self.data.remove(key);
}
fn clear_expired(&self) {
let now = Instant::now();
self.data.retain(|_, entry| entry.expires_at > now);
}
fn len(&self) -> usize {
self.data.len()
}
}
fn main() {
let cache = Arc::new(Cache::new());
// Set some values
cache.set("user:1".to_string(), "Alice".to_string(), Duration::from_secs(5));
cache.set("user:2".to_string(), "Bob".to_string(), Duration::from_millis(100));
// Retrieve values
println!("user:1 = {:?}", cache.get("user:1"));
println!("user:2 = {:?}", cache.get("user:2"));
thread::sleep(Duration::from_millis(150));
// user:2 should be expired
println!("After 150ms - user:2 = {:?}", cache.get("user:2"));
println!("After 150ms - user:1 = {:?}", cache.get("user:1"));
// Clean up expired entries
cache.clear_expired();
println!("Cache size after cleanup: {}", cache.len());
}use dashmap::DashMap;
fn main() {
let map: DashMap<i32, String> = DashMap::new();
for i in 0..20 {
map.insert(i, format!("value-{}", i));
}
// Iterate over shards
println!("Number of shards: {}", map.shards().len());
// Access individual shard (advanced use case)
for (idx, shard) in map.shards().iter().enumerate() {
let guard = shard.read();
println!("Shard {}: {} entries", idx, guard.len());
}
// Iterate over all entries
let mut keys = Vec::new();
for entry in map.iter() {
keys.push(*entry.key());
}
keys.sort();
println!("Keys: {:?}", keys);
// Filter and retain
map.retain(|k, _| *k % 2 == 0);
println!("After retaining even keys: {} entries", map.len());
}use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
fn main() {
let counters = Arc::new(DashMap::<String, u64>::new());
let mut handles = vec![];
// Multiple threads incrementing counters
for i in 0..5 {
let counters_clone = Arc::clone(&counters);
let handle = thread::spawn(move || {
for _ in 0..1000 {
// Atomically increment counter
counters_clone
.entry(format!("counter-{}", i % 3))
.and_modify(|v| *v += 1)
.or_insert(1);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
for entry in counters.iter() {
println!("{}: {}", entry.key(), entry.value());
}
}use dashmap::DashMap;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Instant;
fn main() {
let iterations = 100_000;
// DashMap approach
{
let map = Arc::new(DashMap::<u32, u32>::new());
let start = Instant::now();
let mut handles = vec![];
for _ in 0..4 {
let map_clone = Arc::clone(&map);
handles.push(thread::spawn(move || {
for i in 0..iterations {
map_clone.insert(i % 1000, i);
}
}));
}
for h in handles {
h.join().unwrap();
}
println!("DashMap: {:?}", start.elapsed());
}
// RwLock<HashMap> approach
{
let map = Arc::new(RwLock::new(HashMap::<u32, u32>::new()));
let start = Instant::now();
let mut handles = vec![];
for _ in 0..4 {
let map_clone = Arc::clone(&map);
handles.push(thread::spawn(move || {
for i in 0..iterations {
let mut guard = map_clone.write().unwrap();
guard.insert(i % 1000, i);
}
}));
}
for h in handles {
h.join().unwrap();
}
println!("RwLock<HashMap>: {:?}", start.elapsed());
}
}use dashmap::DashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct UserId {
id: u64,
region: String,
}
#[derive(Debug, Clone)]
struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let users: DashMap<UserId, User> = DashMap::new();
let id1 = UserId { id: 1, region: "us".to_string() };
let id2 = UserId { id: 2, region: "eu".to_string() };
users.insert(id1.clone(), User {
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
active: true,
});
users.insert(id2.clone(), User {
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
active: false,
});
// Lookup by key
if let Some(user) = users.get(&id1) {
println!("Found: {} ({})", user.name, user.email);
}
// Update
users.entry(id2.clone()).and_modify(|u| u.active = true);
// List all users
for entry in users.iter() {
println!("{:?}: {:?}", entry.key(), entry.value());
}
}DashMap as a drop-in concurrent replacement for HashMapArc when sharing across threads: Arc::new(DashMap::new())insert(), get(), get_mut(), remove(), contains_key()entry() API for atomic conditional operations like counters and cachesor_insert() and or_insert_with() insert if key is missingand_modify() updates existing values; combine with or_insert() for upsertsiter() — returns guarded referencesretain() filters entries in place; clear() removes allshards() for advanced introspection of internal segmentsRwLock<HashMap> under contention due to shardingHash + Eq; values can be any Sized type