Einleitung: Warum Go einfach, aber nicht leicht ist
\n\nDie Sprache Go (Golang) wird oft für ihre Einfachheit und Prägnanz gelobt. Doch genau diese Einfachheit führt Anfänger, die aus „wortreicheren" Sprachen wie Python oder Java kommen, nicht selten in die Irre. In diesem Artikel haben wir die Top Fehler von Anfängern in Go zusammengestellt – von typischen Problemen bei der Fehlerbehandlung bis hin zu nicht offensichtlichen Fallstricken bei der Arbeit mit Goroutinen. Wenn Sie gerade erst Ihre Reise in der Welt von Golang beginnen, hilft Ihnen diese Zusammenstellung, nicht in dieselben Fallen zu tappen wie viele vor Ihnen.
\n\n1. Ignorieren von Fehlern und falsche Behandlung
\n\nDer häufigste Fehler in Go ist das Ignorieren von zurückgegebenen Fehlern. Im Gegensatz zu Exceptions in Python oder Java verwendet Go eine explizite Fehlerprüfung über den Rückgabewert. Anfänger schreiben oft:
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n // ❌ FEHLER: Fehler ignorieren\n file, _ := os.Open("file.txt")\n fmt.Println(file.Name())\n}\n\n\nHier sagt das Symbol _ (leerer Bezeichner) dem Compiler buchstäblich: „Der Fehler ist mir egal". In einem echten Projekt führt dies zu einer Panik oder fehlerhaften Funktionsweise. Der richtige Ansatz ist, den Fehler immer zu prüfen:
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("file.txt")\n if err != nil {\n // ✅ Fehler behandeln\n fmt.Println("Fehler beim Öffnen der Datei:", err)\n return\n }\n defer file.Close()\n fmt.Println("Datei erfolgreich geöffnet:", file.Name())\n}\n\n\nTipp: Verwenden Sie _ niemals für Fehler, es sei denn, Sie sind sich zu 100 % sicher, dass ein Fehler unmöglich ist. Es ist besser, eine zusätzliche Zeile if err != nil zu schreiben, als nachts vor der Deadline einen Bug zu suchen.
2. Probleme mit Goroutinen: Datenrennen und Lecks
\n\nGo ist bekannt für seine Einfachheit bei der Arbeit mit Nebenläufigkeit, aber genau das schafft auch Fallstricke. Anfänger vergessen oft die Synchronisation des Zugriffs auf gemeinsame Daten.
\n\n2.1. Datenrennen (Data Race)
\n\nStellen Sie sich vor, mehrere Goroutinen schreiben gleichzeitig in dieselbe 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 // ❌ FEHLER: Datenrennen\n counter++\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Ergebnis ist unvorhersehbar\n}\n\n\nDas Ergebnis ist bei jedem Durchlauf unterschiedlich. Lösung: Verwenden Sie einen Mutex oder atomare Operationen:
\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 // ✅ Atomare Addition verwenden\n atomic.AddInt64(&counter, 1)\n wg.Done()\n }()\n }\n wg.Wait()\n fmt.Println("Counter:", counter) // Immer 1000\n}\n\n\n2.2. Goroutinen-Leck
\n\nEin weiterer klassischer Fehler ist es, eine Goroutine zu starten und sie zu vergessen. Wenn eine Goroutine auf Daten aus einem Kanal wartet, der nie geschlossen wird, hängt sie für immer und verbraucht Speicher:
\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) // ❌ FEHLER: Goroutine wird nie beendet\n ch <- 1\n ch <- 2\n // Kanal nicht geschlossen -> Leck\n time.Sleep(time.Second)\n}\n\n\nLösung: Schließen Sie den Kanal immer, wenn Sie mit dem Senden von Daten fertig sind, oder verwenden Sie einen Kontext zum Abbrechen.
\n\n3. Falsche Verwendung von Nil-Slices und Maps
\n\nIn Go sind n