Loading page…
Rust walkthroughs
Loading page…
DashMap is a blazing fast concurrent map that provides thread-safe access without requiring explicit locking. It uses sharding (partitioning data across multiple inner HashMaps) to minimize lock contention, making it significantly faster than wrapping a HashMap in a Mutex or RwLock for concurrent access.
Key concepts:
When to use DashMap:
When NOT to use DashMap:
use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
fn main() {
let map = Arc::new(DashMap::new());
// Spawn multiple threads writing to the map
let mut handles = vec![];
for i in 0..5 {
let map = Arc::clone(&map);
handles.push(thread::spawn(move || {
map.insert(i, format!("value-{}", i));
}));
}
for handle in handles {
handle.join().unwrap();
}
println!("Map contents: {:?}", map);
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("apple", 3);
map.insert("banana", 2);
map.insert("cherry", 5);
// Get value (returns reference guard)
if let Some(value) = map.get(&"apple") {
println!("Apple: {}", *value);
}
// Check if key exists
println!("Contains 'banana': {}", map.contains_key(&"banana"));
// Get key-value pair
if let Some(entry) = map.get_key_value(&"cherry") {
println!("Key: {}, Value: {}", entry.0, entry.1);
}
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("counter", 0);
// Get mutable reference
if let Some(mut value) = map.get_mut(&"counter") {
*value += 1;
}
println!("Counter: {:?}", map.get(&"counter"));
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
// Insert if not present
map.entry("apple").or_insert(5);
map.entry("apple").or_insert(10); // Won't overwrite
// Insert with default function
map.entry("banana").or_insert_with(|| {
println!("Creating default for banana");
3
});
// Modify existing entry
map.entry("apple").and_modify(|v| *v += 1);
println!("Map: {:?}", map);
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Remove by key
let removed = map.remove(&"b");
println!("Removed: {:?}", removed);
// Remove if matches condition
map.remove_if(&"c", |_, v| *v > 5);
println!("'c' still exists: {}", map.contains_key(&"c"));
// Remove entry
let entry = map.remove_entry(&"a");
println!("Removed entry: {:?}", entry);
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Iterate over entries (locks each shard as needed)
for entry in map.iter() {
println!("{} = {}", entry.key(), entry.value());
}
// Iterate over keys
for key in map.iter().map(|e| e.key().clone()).collect::<Vec<_>>() {
println!("Key: {}", key);
}
}use dashmap::DashMap;
fn main() {
// Create with specific number of shards (must be power of 2)
let map: DashMap<i32, String> = DashMap::with_shard_amount(16);
// More shards = less contention, more memory overhead
println!("Shards: {}", map.shards().len());
}use dashmap::DashMap;
fn main() {
// Create with capacity
let map: DashMap<i32, String> = DashMap::with_capacity(100);
println!("Capacity: {}", map.capacity());
println!("Len: {}", map.len());
for i in 0..50 {
map.insert(i, format!("value-{}", i));
}
println!("After inserts - Len: {}, Capacity: {}", map.len(), map.capacity());
// Shrink to fit
map.shrink_to_fit();
println!("After shrink - Capacity: {}", map.capacity());
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("a", 1);
map.insert("b", 2);
println!("Is empty: {}", map.is_empty());
println!("Len: {}", map.len());
map.clear();
println!("After clear - Is empty: {}", map.is_empty());
}use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
fn main() {
let counters = Arc::new(DashMap::new());
let mut handles = vec![];
// Spawn threads that increment counters
for i in 0..10 {
let counters = Arc::clone(&counters);
handles.push(thread::spawn(move || {
for j in 0..100 {
let key = format!("counter-{}", j % 3);
counters.entry(key).and_modify(|v| *v += 1).or_insert(1);
}
}));
}
for handle in handles {
handle.join().unwrap();
}
println!("Final counts:");
for entry in counters.iter() {
println!(" {}: {}", entry.key(), entry.value());
}
}use dashmap::DashMap;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
struct CachedResponse {
body: String,
cached_at: Instant,
}
struct RequestCache {
cache: DashMap<String, CachedResponse>,
ttl: Duration,
}
impl RequestCache {
fn new(ttl_secs: u64) -> Self {
Self {
cache: DashMap::new(),
ttl: Duration::from_secs(ttl_secs),
}
}
fn get(&self, url: &str) -> Option<String> {
self.cache.get(url).and_then(|entry| {
if entry.cached_at.elapsed() < self.ttl {
Some(entry.body.clone())
} else {
None
}
})
}
fn set(&self, url: String, body: String) {
self.cache.insert(url, CachedResponse {
body,
cached_at: Instant::now(),
});
}
fn invalidate(&self, url: &str) {
self.cache.remove(url);
}
}
fn main() {
let cache = Arc::new(RequestCache::new(60));
cache.set("https://example.com".to_string(), "Hello".to_string());
if let Some(body) = cache.get("https://example.com") {
println!("Cached: {}", body);
}
}use dashmap::DashMap;
use std::time::Instant;
struct RateLimiter {
requests: DashMap<String, (u32, Instant)>,
max_requests: u32,
window_secs: u64,
}
impl RateLimiter {
fn new(max_requests: u32, window_secs: u64) -> Self {
Self {
requests: DashMap::new(),
max_requests,
window_secs,
}
}
fn check(&self, client_id: &str) -> bool {
let now = Instant::now();
let window = std::time::Duration::from_secs(self.window_secs);
self.requests.entry(client_id.to_string())
.and_modify(|(count, time)| {
if now.duration_since(*time) > window {
*count = 1;
*time = now;
} else {
*count += 1;
}
})
.or_insert((1, now));
self.requests.get(client_id)
.map(|entry| entry.0 <= self.max_requests)
.unwrap_or(true)
}
}
fn main() {
let limiter = RateLimiter::new(3, 60);
println!("Request 1: {}", limiter.check("client-1"));
println!("Request 2: {}", limiter.check("client-1"));
println!("Request 3: {}", limiter.check("client-1"));
println!("Request 4: {}", limiter.check("client-1")); // Should be false
}use dashmap::DashMap;
use std::sync::Arc;
#[derive(Clone)]
struct Session {
user_id: u64,
username: String,
created_at: std::time::Instant,
}
struct SessionStore {
sessions: DashMap<String, Session>,
}
impl SessionStore {
fn new() -> Self {
Self {
sessions: DashMap::new(),
}
}
fn create_session(&self, user_id: u64, username: String) -> String {
let session_id = format!("session-{}-{}", user_id, uuid::Uuid::new_v4());
self.sessions.insert(session_id.clone(), Session {
user_id,
username,
created_at: std::time::Instant::now(),
});
session_id
}
fn get_session(&self, session_id: &str) -> Option<Session> {
self.sessions.get(session_id).map(|s| s.clone())
}
fn remove_session(&self, session_id: &str) {
self.sessions.remove(session_id);
}
fn active_sessions(&self) -> usize {
self.sessions.len()
}
}
fn main() {
let store = SessionStore::new();
let session_id = store.create_session(123, "alice".to_string());
println!("Created session: {}", session_id);
if let Some(session) = store.get_session(&session_id) {
println!("User: {}", session.username);
}
store.remove_session(&session_id);
println!("Active sessions: {}", store.active_sessions());
}use dashmap::DashMap;
fn main() {
let map: DashMap<i32, String> = DashMap::with_shard_amount(4);
for i in 0..20 {
map.insert(i, format!("value-{}", i));
}
// Check number of shards
println!("Number of shards: {}", map.shards().len());
// Calculate entries per shard
let mut shard_counts = vec![0; map.shards().len()];
for (i, shard) in map.shards().iter().enumerate() {
shard_counts[i] = shard.read().len();
}
println!("Entries per shard: {:?}", shard_counts);
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Retain only even values
map.retain(|_k, v| *v % 2 == 0);
println!("After retain:");
for entry in map.iter() {
println!(" {} = {}", entry.key(), entry.value());
}
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("counter", 0);
// Compare and update
let updated = map.update(&"counter", |v| *v + 1);
println!("Updated: {:?}", updated);
// Conditional update
map.view(&"counter", |k, v| {
if *v < 10 {
println!("Counter {} is less than 10", k);
}
});
println!("Final value: {:?}", map.get(&"counter"));
}use dashmap::DashMap;
use std::collections::HashSet;
fn main() {
// Store multiple values per key
let map: DashMap<String, HashSet<i32>> = DashMap::new();
map.entry("group-a".to_string())
.or_insert_with(HashSet::new)
.insert(1);
map.entry("group-a".to_string())
.and_modify(|set| { set.insert(2); });
if let Some(set) = map.get(&"group-a".to_string()) {
println!("Group A: {:?}", *set);
}
}use dashmap::DashMap;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::thread;
use std::time::Instant;
fn main() {
const OPERATIONS: usize = 100_000;
const THREADS: usize = 8;
// DashMap benchmark
let dash_map = Arc::new(DashMap::new());
let start = Instant::now();
let mut handles = vec![];
for _ in 0..THREADS {
let map = Arc::clone(&dash_map);
handles.push(thread::spawn(move || {
for i in 0..OPERATIONS / THREADS {
map.insert(i, i);
let _ = map.get(&i);
}
}));
}
for h in handles {
h.join().unwrap();
}
println!("DashMap: {:?}", start.elapsed());
// Mutex<HashMap> benchmark
let mutex_map = Arc::new(Mutex::new(HashMap::new()));
let start = Instant::now();
let mut handles = vec![];
for _ in 0..THREADS {
let map = Arc::clone(&mutex_map);
handles.push(thread::spawn(move || {
for i in 0..OPERATIONS / THREADS {
map.lock().unwrap().insert(i, i);
let _ = map.lock().unwrap().get(&i);
}
}));
}
for h in handles {
h.join().unwrap();
}
println!("Mutex<HashMap>: {:?}", start.elapsed());
}use dashmap::DashMap;
fn main() {
let map = DashMap::new();
// Batch insert
let items = vec![("a", 1), ("b", 2), ("c", 3)];
for (k, v) in items {
map.insert(k, v);
}
// Batch remove
let keys = vec!["a", "b"];
for k in keys {
map.remove(k);
}
println!("Remaining: {:?}", map.iter().collect::<Vec<_>>());
}DashMap Type Signature:
use dashmap::DashMap;
let map: DashMap<K, V> = DashMap::new();Key Methods:
| Method | Description |
|--------|-------------|
| insert(k, v) | Insert key-value pair |
| get(&k) | Get value (returns guard) |
| get_mut(&k) | Get mutable value |
| contains_key(&k) | Check if key exists |
| remove(&k) | Remove by key |
| entry(k) | Entry API |
| iter() | Iterate over entries |
| len() | Number of entries |
| is_empty() | Check if empty |
| clear() | Remove all entries |
| retain(f) | Filter entries |
| shards() | Access inner shards |
DashMap vs Other Concurrent Maps:
| Feature | DashMap | Mutex | RwLock | |---------|---------|----------------|-----------------| | Read parallelism | Yes | No | Yes | | Write parallelism | Yes | No | No | | Lock granularity | Per-shard | Global | Global | | Memory overhead | Higher | Lower | Lower | | Contention | Low | High | Medium |
Time Complexity:
| Operation | Complexity | |-----------|------------| | Insert | O(1) | | Get | O(1) | | Remove | O(1) | | Iterate | O(n) |
Key Points:
get() returns a guard that holds a read lockget_mut() returns a guard that holds a write lock