How to Use sync.Map in Go: Simple Guide with Examples
In Go,
sync.Map is a concurrent map designed for safe access by multiple goroutines without explicit locking. You use methods like Store, Load, and Delete to add, retrieve, and remove items safely. It is ideal for cases where many goroutines read, write, and delete keys concurrently.Syntax
The sync.Map type provides a concurrent map with these main methods:
Store(key, value): Adds or updates a key-value pair.Load(key): Retrieves the value for a key and a boolean indicating if it was found.Delete(key): Removes a key and its value.LoadOrStore(key, value): Loads the existing value or stores and returns the new one.Range(func(key, value) bool): Iterates over all key-value pairs.
This map is safe for concurrent use without extra locks.
go
var m sync.Map // Store a value m.Store("foo", 42) // Load a value value, ok := m.Load("foo") // Delete a key m.Delete("foo") // Load or store actual, loaded := m.LoadOrStore("foo", 100) // Iterate all m.Range(func(key, value interface{}) bool { fmt.Println(key, value) return true // continue iteration })
Example
This example shows how to safely store, load, and delete keys in sync.Map concurrently.
go
package main import ( "fmt" "sync" ) func main() { var m sync.Map // Store some values m.Store("apple", 5) m.Store("banana", 10) // Load and print a value if val, ok := m.Load("apple"); ok { fmt.Println("apple:", val) } else { fmt.Println("apple not found") } // Delete a key m.Delete("banana") // Try to load deleted key if _, ok := m.Load("banana"); !ok { fmt.Println("banana deleted") } // Iterate all keys m.Range(func(key, value interface{}) bool { fmt.Printf("%v -> %v\n", key, value) return true }) }
Output
apple: 5
banana deleted
apple -> 5
Common Pitfalls
Common mistakes when using sync.Map include:
- Using it like a regular map without understanding it is optimized for concurrent use cases.
- Expecting type safety:
sync.Mapstores keys and values asinterface{}, so you must type assert when loading. - Using
Rangeto modify the map concurrently can cause unexpected behavior. - Overusing
sync.Mapfor simple cases where a normal map with a mutex is more efficient.
Example of type assertion:
go
var m sync.Map m.Store("key", 123) // Wrong: assuming value is int without check // val := m.Load("key").(int) // panics if key missing // Right: check type assertion val, ok := m.Load("key") if ok { if num, ok := val.(int); ok { fmt.Println("Value is", num) } else { fmt.Println("Value is not an int") } } else { fmt.Println("Key not found") }
Quick Reference
| Method | Description |
|---|---|
| Store(key, value) | Add or update a key-value pair |
| Load(key) | Retrieve value and existence boolean |
| Delete(key) | Remove a key and its value |
| LoadOrStore(key, value) | Load existing or store new value |
| Range(func(key, value) bool) | Iterate all key-value pairs |
Key Takeaways
Use sync.Map for safe concurrent access without explicit locks.
Always check type assertions when loading values from sync.Map.
Avoid modifying the map inside Range to prevent unexpected behavior.
sync.Map is best for cases with many goroutines reading and writing concurrently.
For simple or single-threaded cases, a normal map with a mutex may be more efficient.