Introduction : Pourquoi Go est simple, mais pas facile
\n\nLe langage Go (Golang) est souvent loué pour sa simplicité et sa concision. Cependant, c'est précisément cette simplicité qui piège souvent les débutants venant de langages plus « verbeux » comme Python ou Java. Dans cet article, nous avons rassemblé les top erreurs des développeurs Go débutants — des problèmes typiques de gestion des erreurs aux pièges moins évidents lors du travail avec les goroutines. Si vous débutez votre parcours dans le monde de Golang, cette sélection vous aidera à éviter les mêmes pièges que beaucoup avant vous.
\n\n1. Ignorer les erreurs et mauvaise gestion
\n\nL'erreur la plus courante en Go est d'ignorer les erreurs retournées. Contrairement aux exceptions en Python ou Java, Go utilise une vérification explicite des erreurs via la valeur de retour. Les débutants écrivent souvent :
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n // ❌ ERREUR : nous ignorons l'erreur\n file, _ := os.Open("file.txt")\n fmt.Println(file.Name())\n}\n\n\nIci, le symbole _ (identifiant vide) dit littéralement au compilateur : « Je me fiche de l'erreur ». Dans un projet réel, cela entraînera une panique ou un fonctionnement incorrect. L'approche correcte est de toujours vérifier l'erreur :
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("file.txt")\n if err != nil {\n // ✅ Nous gérons l'erreur\n fmt.Println("Erreur d'ouverture du fichier :", err)\n return\n }\n defer file.Close()\n fmt.Println("Fichier ouvert avec succès :", file.Name())\n}\n\n\nConseil : N'utilisez jamais _ pour les erreurs, sauf si vous êtes sûr à 100 % que l'erreur est impossible. Mieux vaut écrire une ligne supplémentaire if err != nil que de chercher un bug la nuit avant une échéance.
2. Problèmes avec les goroutines : course aux données et fuites
\n\nGo est réputé pour sa simplicité dans le travail avec la concurrence, mais cela crée aussi des pièges. Les débutants oublient souvent la synchronisation de l'accès aux données partagées.
\n\n2.1. Course aux données (Data Race)
\n\nImaginez que plusieurs goroutines écrivent simultanément dans une même variable :
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc main() {\n counter := 0\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ❌ ERREUR : course aux données\n counter++\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Résultat imprévisible\n}\n\n\nLe résultat sera différent à chaque exécution. Solution : utiliser un mutex ou des opérations atomiques :
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n "sync/atomic"\n)\n\nfunc main() {\n var counter int64\n var wg sync.WaitGroup\n\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go func() {\n // ✅ Utilisation de l'addition atomique\n atomic.AddInt64(&counter, 1)\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Toujours 1000\n}\n\n\n2.2. Fuite de goroutines
\n\nUne autre erreur classique est de lancer une goroutine et de l'oublier. Si une goroutine attend des données d'un canal qui ne sera jamais fermé, elle restera suspendue indéfiniment, consommant de la mémoire :
\n\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc worker(ch chan int) {\n for msg := range ch {\n fmt.Println(msg)\n }\n}\n\nfunc main() {\n ch := make(chan int)\n go worker(ch) // ❌ ERREUR : la goroutine ne se terminera jamais\n ch <- 1\n ch <- 2\n // Canal non fermé -> fuite\n time.Sleep(time.Second)\n}\n\n\nSolution : fermez toujours le canal lorsque vous avez fini d'envoyer des données, ou utilisez un contexte pour l'annulation.
\n\n3. Mauvaise utilisation des slices nil et des maps
\n\nEn Go, n