Comparaison de struct, slice et map en Go : du débutant au pro
\n\nAvez-vous déjà écrit du code Go qui devait comparer deux structures, deux slices ou deux maps, et obtenu une erreur de compilation : invalid operation: cannot compare ? Si oui, vous n'êtes pas seul. C'est l'une des questions les plus populaires sur Stack Overflow (des dizaines de milliers de vues !), qui déroute même les développeurs expérimentés passés à Go depuis d'autres langages.
Dans cet article, nous allons une fois pour toutes comprendre comment comparer correctement les types de données composites en Go. Vous apprendrez pourquoi == ne fonctionne pas toujours, quand utiliser reflect.DeepEqual, et comment écrire votre propre fonction de comparaison efficace. C'est parti !
Pourquoi la comparaison simple avec == ne fonctionne-t-elle pas toujours ?
\n\nEn Go, l'opérateur == ne fonctionne que pour les types comparables (comparable). Ceux-ci incluent :
- \n
- Les types de base :
int,string,bool,float, etc. \n - Les pointeurs (
pointer) \n - Les canaux (
channel) \n - Les interfaces (si les types dynamiques sont comparables) \n
- Les structures dont tous les champs sont comparables \n
- Les tableaux (
array), si l'élément du tableau est comparable \n
En revanche, les slices et les maps ne sont pas comparables directement avec ==. Tenter de le faire entraînera une erreur de compilation. Pourquoi ? Parce qu'un slice est un type de référence qui inclut un pointeur vers les données, la longueur et la capacité. Deux slices peuvent être "égaux" en contenu, mais avoir des structures internes différentes.
package main\n\nimport "fmt"\n\nfunc main() {\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n \n // Erreur de compilation !\n // fmt.Println(a == b) \n \n fmt.Println("Fonctionne uniquement avec nil :")\n fmt.Println(a == nil) // false\n fmt.Println(b == nil) // false\n}\n\nIl en va de même pour les maps. La seule chose que vous pouvez vérifier avec == est l'égalité avec nil.
Comparaison des structures (struct) en Go
\n\nLa comparaison des structures est le cas le plus délicat. Si tous les champs de votre structure sont de types comparables, alors == fonctionne !
type Person struct {\n Name string\n Age int\n}\n\nfunc main() {\n p1 := Person{"Alice", 30}\n p2 := Person{"Alice", 30}\n \n fmt.Println(p1 == p2) // true — ça marche !\n}\n\nMais le problème survient lorsque la structure contient un slice ou une map :
\n\ntype PersonWithHobbies struct {\n Name string\n Age int\n Hobbies []string // slice — type non comparable\n}\n\nfunc main() {\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n \n // Erreur de compilation !\n // fmt.Println(p1 == p2)\n}\n\nDans ce cas, le compilateur Go générera une erreur : struct containing []string cannot be compared.
Solution n°1 : reflect.DeepEqual — universel mais lent
\n\nLe moyen le plus simple de comparer des types complexes est d'utiliser la fonction reflect.DeepEqual de la bibliothèque standard de Go. Elle parcourt récursivement les structures, slices et maps et compare leur contenu.
import (\n "fmt"\n "reflect"\n)\n\nfunc main() {\n // Comparaison de slices\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n fmt.Println(reflect.DeepEqual(a, b)) // true\n \n // Comparaison de maps\n m1 := map[string]int{"a": 1, "b": 2}\n m2 := map[string]int{"a": 1, "b": 2}\n fmt.Println(reflect.DeepEqual(m1, m2)) // true\n \n // Comparaison de structures avec des champs non comparables\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n fmt.Println(reflect.DeepEqual(p1, p2)) // true\n}\n\n<