The Eternal Headache of a Go Developer: How to Quickly Get All Keys from a Map?
\nHave you ever written something like this in Go?
\nm := map[string]int{"go": 1, "python": 2, "rust": 3}\nkeys := make([]string, 0, len(m))\nfor k := range m {\n keys = append(keys, k)\n}\n\nIf yes — you are not alone. Getting a slice of keys from a map is one of the most popular questions on Stack Overflow in the Go category. The question gets thousands of views, with dozens of answers offering different approaches. And it's no wonder: Go doesn't have a built-in keys() function like Python or JavaScript. You have to reinvent the wheel every time.
But 2025 brought changes. With the release of Go 1.22 and generics, along with new idioms, more elegant solutions have emerged. In this article, we'll break down all the ways to get a slice of keys from a map — from the classics to modern best practices. Let's go!
\n\nWhy Isn't There a Built-in keys() in Go?
\nBefore jumping into the code, let's understand the language's philosophy. Go is minimalist. Its creators deliberately avoid adding functions that can be implemented in a single line. for k := range m is the built-in way to iterate over a map. But the problem is that:
- \n
- The order of keys is not guaranteed (and this is a feature, not a bug) \n
- There is no syntactic sugar for "collect everything into a slice" \n
- Generics only appeared in 1.18 and became stable by 1.22 \n
That's why the community has developed several patterns. Let's compare them.
\n\nMethod 1: Classic Loop (Basic Level)
\nThe simplest and most understandable way is the one we showed at the beginning. It works in any version of Go.
\nfunc Keys(m map[string]int) []string {\n keys := make([]string, 0, len(m))\n for k := range m {\n keys = append(keys, k)\n }\n return keys\n}\n\nPros:
\n- \n
- Transparency — any beginner will understand what's happening \n
- No dependency on the Go version \n
- Easy to modify (e.g., add filtering) \n
Cons:
\n- \n
- Lots of boilerplate for each map \n
- You have to write a separate function for each type \n
Verdict: Great for educational examples or one-off tasks. But in production code, you want universality.
\n\nMethod 2: Generics (Go 1.18+)
\nWith the advent of generics, it became possible to write one function for any key type.
\nfunc Keys[K comparable, V any](m map[K]V) []K {\n keys := make([]K, 0, len(m))\n for k := range m {\n keys = append(keys, k)\n }\n return keys\n}\n\n// Usage:\nm := map[string]int{"a": 1, "b": 2}\nkeys := Keys(m) // []string{"a", "b"}\n\nBeautiful, right? One function — and everything works. Go 1.22 made generics even more stable, so this method is recommended for new projects.
\nImportant: keys must be comparable. This is automatically satisfied for all standard types (string, int, float, struct without slices, etc.).
Method 3: Using reflect (For Exotic Cases)
\nSometimes you need to work with a map whose type is only known at runtime. This is where the reflect package comes to the rescue.
func KeysReflect(m interface{}) []interface{} {\n v := reflect.ValueOf(m)\n if v.Kind() != reflect.Map {\n return nil\n }\n keys := make([]interface{}, v.Len())\n for i, k := range v.MapKeys() {\n keys[i] = k.Interface()\n }\n return keys\n}\n\nWhen to use:
\n- \n
- When serializing/deserializing unknown structures \n
- In general-purpose libraries \n
- When the map type comes from an external source (JSON, YAML)