Loading page…
Rust walkthroughs
Loading page…
The dashmap crate provides a blazing-fast concurrent hash map for Rust. Unlike std::collections::HashMap which requires external locking for concurrent access, DashMap handles synchronization internally using sharded locking—dividing the map into segments (shards), each with its own lock. This allows multiple threads to access different parts of the map simultaneously without contention. It's perfect for high-throughput concurrent applications like caches, registries, and shared state.
Key concepts:
entry API and references for complex operations# Cargo.toml
[dependencies]
dashmap = "5"use dashmap::DashMap;
fn main() {
let map = DashMap::new();
map.insert("key", "value");
if let Some(value) = map.get("key") {
println!("Found: {}", *value);
}
}use dashmap::DashMap;
fn main() {
// Create a new DashMap
let map: DashMap<i32, &str> = DashMap::new();
// Create with capacity hint
let map_with_capacity: DashMap<i32, String> = DashMap::with_capacity(100);
// Insert values
map.insert(1, "one");
map.insert(2, "two");
map.insert(3, "three");
// Get values (returns a reference wrapper)
if let Some(value) = map.get(&1) {
println!("Key 1: {}", *value);
}
// Check if key exists
println!("Contains 2: {}", map.contains_key(&2));
// Remove a value
let removed = map.remove(&3);
println!("Removed: {:?}", removed);
// Length
println!("Length: {}", map.len());
// Clear all entries
map.clear();
println!("After clear: {}", map.len());
}use dashmap::DashMap;
use std::thread;
fn main() {
let map = DashMap::new();
// Spawn multiple writers
let mut handles = vec![];
for i in 0..10 {
let map = map.clone(); // DashMap is cheaply cloneable (Arc internally)
let handle = thread::spawn(move || {
for j in 0..100 {
map.insert(i * 100 + j, format!("value_{}_{}", i, j));
}
});
handles.push(handle);
}
// Wait for all writers
for handle in handles {
handle.join().unwrap();
}
println!("Total entries: {}", map.len());
// Spawn multiple readers
let mut readers = vec![];
for i in 0..5 {
let map = map.clone();
let handle = thread::spawn(move || {
let start = i * 200;
let end = start + 100;
for key in start..end {
if let Some(value) = map.get(&key) {
println!("Thread {} found key {}: {}", i, key, *value);
}
}
});
readers.push(handle);
}
for handle in readers {
handle.join().unwrap();
}
}use dashmap::DashMap;
fn main() {
let map: DashMap<&str, i32> = DashMap::new();
// Get or insert default
let entry = map.entry("counter").or_insert(0);
*entry += 1;
drop(entry); // Must drop the reference to release lock
// Get or insert with function
map.entry("items")
.or_insert_with(|| vec!["initial".to_string()]);
// Check and modify atomically
map.entry("counter")
.and_modify(|v| *v += 1)
.or_insert(1);
// Get current value
if let Some(counter) = map.get("counter") {
println!("Counter: {}", *counter);
}
}use dashmap::DashMap;
fn main() {
let map: DashMap<String, i32> = DashMap::new();
map.insert("score".to_string(), 100);
// Method 1: Get, modify, drop (not atomic!)
if let Some(mut score) = map.get_mut("score") {
*score += 10;
}
// Method 2: Using alter for atomic update
map.alter("score", |_, v| v + 5);
// Method 3: Using entry API
map.entry("score".to_string())
.and_modify(|v| *v += 5)
.or_insert(0);
println!("Final score: {:?}", map.get("score"));
}use dashmap::DashMap;
fn main() {
let map: DashMap<i32, String> = DashMap::new();
map.insert(1, "hello".to_string());
map.insert(2, "world".to_string());
// get() returns a reference guard (like RwLock's RwLockReadGuard)
if let Some(reference) = map.get(&1) {
// reference is a Ref<'_, K, V>
let key = reference.key();
let value = reference.value();
println!("Key: {}, Value: {}", key, value);
}
// get_mut() returns a mutable reference
if let Some(mut reference) = map.get_mut(&2) {
reference.push('!');
}
println!("Updated: {:?}", map.get(&2));
// Reference is automatically released when dropped
}use dashmap::DashMap;
fn main() {
let map: DashMap<&str, i32> = DashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Immutable iteration
println!("Iterating:");
for entry in map.iter() {
println!(" {} = {}", entry.key(), entry.value());
}
// Mutable iteration
println!("\nMutating values:");
for mut entry in map.iter_mut() {
*entry.value_mut() *= 10;
println!(" {} = {}", entry.key(), entry.value());
}
// Converting to iterator (locks entire map briefly)
let entries: Vec<_> = map.into_iter().collect();
println!("\nCollected: {:?}", entries);
}use dashmap::DashMap;
fn main() {
// Default is number of CPU cores * 4 shards
let map: DashMap<i32, i32> = DashMap::new();
println!("Default shards: {}", map.shards().len());
// Create with specific number of shards
let map: DashMap<i32, i32> = DashMap::with_shard_amount(32);
println!("Custom shards: {}", map.shards().len());
// Create with hasher
use std::collections::hash_map::RandomState;
let map: DashMap<i32, i32, RandomState> =
DashMap::with_hasher(RandomState::new());
// Access individual shard (advanced use)
// shards() returns a slice of the internal RwLocks
println!("Shard count: {}", map.shards().len());
}use dashmap::DashMap;
use std::time::{Duration, Instant};
use std::thread;
struct CacheEntry {
value: String,
expires_at: Instant,
}
struct Cache {
data: DashMap<String, CacheEntry>,
default_ttl: Duration,
}
impl Cache {
fn new(default_ttl: Duration) -> Self {
Cache {
data: DashMap::new(),
default_ttl,
}
}
fn get(&self, key: &str) -> Option<String> {
self.data.get(key).and_then(|entry| {
if entry.value.expires_at > Instant::now() {
Some(entry.value.value.clone())
} else {
None // Expired
}
})
}
fn set(&self, key: String, value: String) {
let entry = CacheEntry {
value,
expires_at: Instant::now() + self.default_ttl,
};
self.data.insert(key, entry);
}
fn set_with_ttl(&self, key: String, value: String, ttl: Duration) {
let entry = CacheEntry {
value,
expires_at: Instant::now() + ttl,
};
self.data.insert(key, entry);
}
fn delete(&self, key: &str) {
self.data.remove(key);
}
fn cleanup_expired(&self) {
let now = Instant::now();
let expired_keys: Vec<_> = self.data
.iter()
.filter(|entry| entry.value().expires_at <= now)
.map(|entry| entry.key().clone())
.collect();
for key in expired_keys {
self.data.remove(&key);
}
}
}
fn main() {
let cache = Cache::new(Duration::from_secs(60));
// Concurrent writes
let mut handles = vec![];
for i in 0..5 {
let cache = Cache {
data: cache.data.clone(),
default_ttl: cache.default_ttl,
};
handles.push(thread::spawn(move || {
cache.set(
format!("key_{}", i),
format!("value_{}", i)
);
}));
}
for handle in handles {
handle.join().unwrap();
}
// Concurrent reads
for i in 0..5 {
if let Some(value) = cache.get(&format!("key_{}", i)) {
println!("Found: {}", value);
}
}
}use dashmap::DashMap;
use std::time::{Duration, Instant};
struct RateLimiter {
requests: DashMap<String, (u32, Instant)>,
max_requests: u32,
window: Duration,
}
impl RateLimiter {
fn new(max_requests: u32, window: Duration) -> Self {
RateLimiter {
requests: DashMap::new(),
max_requests,
window,
}
}
fn is_allowed(&self, client_id: &str) -> bool {
let now = Instant::now();
let mut entry = self.requests.entry(client_id.to_string());
if let Some(existing) = entry.get_mut() {
let (count, start_time) = existing.value_mut();
if now.duration_since(*start_time) > self.window {
// Reset window
*count = 1;
*start_time = now;
true
} else if *count < self.max_requests {
*count += 1;
true
} else {
false
}
} else {
entry.insert((1, now));
true
}
}
fn remaining(&self, client_id: &str) -> Option<u32> {
self.requests.get(client_id).map(|entry| {
let (count, start_time) = entry.value();
if Instant::now().duration_since(*start_time) > self.window {
self.max_requests
} else {
self.max_requests.saturating_sub(*count)
}
})
}
}
fn main() {
let limiter = RateLimiter::new(3, Duration::from_secs(60));
let client = "user_123";
for i in 0..5 {
if limiter.is_allowed(client) {
let remaining = limiter.remaining(client).unwrap();
println!("Request {} allowed ({} remaining)", i + 1, remaining);
} else {
println!("Request {} rate limited!", i + 1);
}
}
}use dashmap::DashMap;
use std::sync::Arc;
use std::collections::VecDeque;
#[derive(Clone)]
struct Message {
topic: String,
payload: String,
}
class PubSub {
subscribers: DashMap<String, VecDeque<Message>>,
}
impl PubSub {
fn new() -> Self {
PubSub {
subscribers: DashMap::new(),
}
}
fn subscribe(&self, topic: &str) {
self.subscribers
.entry(topic.to_string())
.or_insert_with(VecDeque::new);
}
fn publish(&self, topic: &str, message: String) {
if let Some(mut queue) = self.subscribers.get_mut(topic) {
queue.push_back(Message {
topic: topic.to_string(),
payload: message,
});
}
}
fn receive(&self, topic: &str) -> Option<Message> {
self.subscribers.get_mut(topic).and_then(|mut queue| {
queue.pop_front()
})
}
}
fn main() {
let pubsub = PubSub::new();
pubsub.subscribe("news");
pubsub.subscribe("alerts");
pubsub.publish("news", "Breaking: Rust 2.0 announced!".to_string());
pubsub.publish("alerts", "System maintenance at 3 AM".to_string());
while let Some(msg) = pubsub.receive("news") {
println!("News: {}", msg.payload);
}
while let Some(msg) = pubsub.receive("alerts") {
println!("Alert: {}", msg.payload);
}
}use dashmap::DashMap;
use std::sync::atomic::{AtomicU64, Ordering};
struct Connection {
id: u64,
created_at: std::time::Instant,
}
impl Connection {
fn new(id: u64) -> Self {
Connection {
id,
created_at: std::time::Instant::now(),
}
}
}
class ConnectionPool {
connections: DashMap<u64, Connection>,
next_id: AtomicU64,
max_connections: usize,
}
impl ConnectionPool {
fn new(max_connections: usize) -> Self {
ConnectionPool {
connections: DashMap::new(),
next_id: AtomicU64::new(1),
max_connections,
}
}
fn acquire(&self) -> Option<u64> {
if self.connections.len() >= self.max_connections {
return None;
}
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
self.connections.insert(id, Connection::new(id));
Some(id)
}
fn release(&self, id: u64) {
self.connections.remove(&id);
}
fn get(&self, id: u64) -> Option<Connection> {
self.connections.get(&id).map(|c| Connection {
id: c.id,
created_at: c.created_at,
})
}
}
fn main() {
let pool = ConnectionPool::new(3);
let conn1 = pool.acquire().unwrap();
let conn2 = pool.acquire().unwrap();
let conn3 = pool.acquire().unwrap();
println!("Active connections: {}", pool.connections.len());
// This should fail - pool is full
match pool.acquire() {
Some(_) => println!("Unexpectedly got connection!"),
None => println!("Pool full, cannot acquire more"),
}
pool.release(conn2);
println!("After release: {}", pool.connections.len());
let conn4 = pool.acquire().unwrap();
println!("Acquired new connection: {}", conn4);
}use dashmap::DashMap;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::Instant;
fn benchmark_dashmap(count: u32) -> u128 {
let map = DashMap::new();
let start = Instant::now();
let mut handles = vec![];
for i in 0..4 {
let map = map.clone();
handles.push(thread::spawn(move || {
for j in 0..count {
map.insert(i * count + j, format!("value_{}", j));
}
}));
}
for handle in handles {
handle.join().unwrap();
}
start.elapsed().as_millis()
}
fn benchmark_mutex(count: u32) -> u128 {
let map = Arc::new(Mutex::new(HashMap::new()));
let start = Instant::now();
let mut handles = vec![];
for i in 0..4 {
let map = map.clone();
handles.push(thread::spawn(move || {
for j in 0..count {
let mut guard = map.lock().unwrap();
guard.insert(i * count + j, format!("value_{}", j));
}
}));
}
for handle in handles {
handle.join().unwrap();
}
start.elapsed().as_millis()
}
fn benchmark_rwlock(count: u32) -> u128 {
let map = Arc::new(RwLock::new(HashMap::new()));
let start = Instant::now();
let mut handles = vec![];
for i in 0..4 {
let map = map.clone();
handles.push(thread::spawn(move || {
for j in 0..count {
let mut guard = map.write().unwrap();
guard.insert(i * count + j, format!("value_{}", j));
}
}));
}
for handle in handles {
handle.join().unwrap();
}
start.elapsed().as_millis()
}
fn main() {
let count = 100_000;
println!("Benchmarking {} writes per thread (4 threads):", count);
println!(" DashMap: {}ms", benchmark_dashmap(count));
println!(" Mutex: {}ms", benchmark_mutex(count));
println!(" RwLock: {}ms", benchmark_rwlock(count));
}use dashmap::DashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, Eq)]
struct UserKey {
tenant_id: u32,
user_id: u32,
}
impl Hash for UserKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.tenant_id.hash(state);
self.user_id.hash(state);
}
}
#[derive(Debug, Clone)]
struct User {
name: String,
email: String,
}
fn main() {
let users: DashMap<UserKey, User> = DashMap::new();
users.insert(UserKey { tenant_id: 1, user_id: 100 }, User {
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
});
users.insert(UserKey { tenant_id: 1, user_id: 101 }, User {
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
});
let key = UserKey { tenant_id: 1, user_id: 100 };
if let Some(user) = users.get(&key) {
println!("User: {} ({})", user.name, user.email);
}
}use dashmap::DashMap;
fn main() {
let map: DashMap<String, Vec<i32>> = DashMap::new();
// Append to existing or create new
map.entry("numbers".to_string())
.or_insert_with(Vec::new)
.push(1);
map.entry("numbers".to_string())
.and_modify(|v| v.push(2))
.or_insert_with(|| vec![2]);
// Another pattern using get_mut
if let Some(mut entry) = map.get_mut("numbers") {
entry.push(3);
}
println!("Numbers: {:?}", map.get("numbers"));
}use dashmap::DashSet;
fn main() {
// DashSet is a DashMap with () values
let set: DashSet<i32> = DashSet::new();
set.insert(1);
set.insert(2);
set.insert(3);
set.insert(1); // Duplicate, ignored
println!("Contains 2: {}", set.contains(&2));
for item in set.iter() {
println!("Item: {}", *item);
}
set.remove(&2);
println!("After removal, contains 2: {}", set.contains(&2));
}use dashmap::DashMap;
fn main() {
let map: DashMap<i32, String> = DashMap::new();
// Batch insert
for i in 0..1000 {
map.insert(i, format!("value_{}", i));
}
// Retain only even keys
map.retain(|k, _| k % 2 == 0);
println!("After retain: {} entries", map.len());
// Shrink to fit
map.shrink_to_fit();
// Remove all matching a predicate
map.remove_if(&50, |_, v| v.starts_with("value"));
}use dashmap::DashMap;
use std::thread;
struct Config {
settings: DashMap<String, String>,
}
impl Config {
fn new() -> Self {
let settings = DashMap::new();
settings.insert("host".to_string(), "localhost".to_string());
settings.insert("port".to_string(), "8080".to_string());
settings.insert("debug".to_string(), "true".to_string());
Config { settings }
}
fn get(&self, key: &str) -> Option<String> {
self.settings.get(key).map(|v| v.clone())
}
fn set(&self, key: String, value: String) {
self.settings.insert(key, value);
}
}
fn main() {
let config = Config::new();
// Multiple readers can access concurrently
let mut handles = vec![];
for i in 0..10 {
let config_settings = config.settings.clone();
handles.push(thread::spawn(move || {
if let Some(host) = config_settings.get("host") {
println!("Thread {}: host = {}", i, *host);
}
if let Some(port) = config_settings.get("port") {
println!("Thread {}: port = {}", i, *port);
}
}));
}
for handle in handles {
handle.join().unwrap();
}
}DashMap::new() creates a new concurrent hash mapinsert(), get(), remove() for basic operationsget() and get_mut() return reference guardsentry() API for atomic check-and-modify operationsiter() and iter_mut() for thread-safe iterationDashSet for set-like behaviorretain() to filter elements atomicallyHashMap<Mutex> for concurrent workloads