Le casse-tête du développeur Go : comment récupérer rapidement toutes les clés d'une map ?
\nAvez-vous déjà écrit en Go quelque chose comme ceci ?
\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\nSi oui, vous n'êtes pas seul. Obtenir un slice des clés d'une map est l'une des questions les plus populaires sur Stack Overflow dans la catégorie Go. La question cumule des milliers de vues, avec des dizaines de réponses proposant différentes approches. Et ce n'est pas surprenant : Go n'a pas de fonction intégrée keys() comme Python ou JavaScript. Il faut à chaque fois réinventer la roue.
Mais 2025 a apporté son lot de changements. Avec l'arrivée de Go 1.22 et des generics, ainsi que de nouvelles idiomes, des solutions plus élégantes ont vu le jour. Dans cet article, nous allons passer en revue toutes les méthodes pour obtenir un slice de clés à partir d'une map — des classiques aux meilleures pratiques modernes. C'est parti !
\n\nPourquoi Go n'a-t-il pas de fonction keys() intégrée ?
\nAvant de plonger dans le code, comprenons la philosophie du langage. Go est minimaliste. Ses créateurs ont délibérément choisi de ne pas ajouter de fonctions qui peuvent être implémentées en une seule ligne. for k := range m est la méthode native pour itérer sur une map. Mais le problème est que :
- \n
- L'ordre des clés n'est pas garanti (et c'est une fonctionnalité, pas un bug) \n
- Il n'y a pas de sucre syntaxique pour « tout rassembler dans un slice » \n
- Les generics ne sont apparus qu'en 1.18, et se sont stabilisés en 1.22 \n
C'est pourquoi la communauté a développé plusieurs patterns. Comparons-les.
\n\nMéthode 1 : La boucle classique (niveau débutant)
\nLa méthode la plus simple et la plus compréhensible est celle que nous avons montrée au début. Elle fonctionne avec n'importe quelle version de 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\nAvantages :
\n- \n
- Transparence — même un débutant comprend ce qui se passe \n
- Aucune dépendance à la version de Go \n
- Facile à modifier (par exemple, ajouter un filtrage) \n
Inconvénients :
\n- \n
- Beaucoup de code boilerplate pour chaque map \n
- Obligation d'écrire une fonction séparée pour chaque type \n
Verdict : Idéal pour des exemples d'apprentissage ou des tâches ponctuelles. Mais dans le code de production, on recherche l'universalité.
\n\nMéthode 2 : Les generics (Go 1.18+)
\nAvec l'arrivée des generics, il est possible d'écrire une seule fonction pour n'importe quel type de clés.
\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// Utilisation :\nm := map[string]int{"a": 1, "b": 2}\nkeys := Keys(m) // []string{"a", "b"}\n\n>Magnifique, n'est-ce pas ? Une seule fonction et tout fonctionne. Go 1.22 a rendu les generics encore plus stables, faisant de cette méthode la recommandée pour les nouveaux projets.\nImportant : les clés doivent être comparable. C'est automatiquement le cas pour tous les types standards (string, int, float, struct sans slices, etc.).
Méthode 3 : Utilisation de reflect (pour les cas exotiques)
\nParfois, il est nécessaire de travailler avec une map dont le type n'est connu qu'à l'exécution. C'est là que le package reflect entre en jeu.
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\nQuand l'utiliser :
\n- \n
- Lors de la sérialisation/désérialisation de structures inconnues \n
- Dans des bibliothèques à usage général \n
- Quand le type de la map provient d'une source externe (JSON, YAML)