Leerer Slice in Go: Korrekte Initialisierung mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Leerer Slice in Go: Wie man von Anfang an keine Fehler macht

\n\n

Jeder Go-Entwickler steht früher oder später vor der Frage: "Wie initialisiert man einen leeren Slice korrekt?". Scheinbar eine einfache Aufgabe, aber so eindeutig ist es nicht. Im offiziellen Go-FAQ und auf Stack Overflow sind diesem Thema hunderte Kommentare und tausende Stimmen gewidmet. Warum?

\n\n

Weil es in Go zwei grundlegend verschiedene Möglichkeiten gibt, einen leeren Slice zu erstellen, und die Wahl zwischen ihnen beeinflusst die Performance, die Lesbarkeit des Codes und sogar das Verhalten bei der Serialisierung. Wenn Sie in Go programmieren und sauberen, effizienten Code ohne Überraschungen schreiben möchten, ist dieser Artikel für Sie.

\n\n

Wir werden analysieren:

\n
    \n
  • Was ist der Unterschied zwischen var s []int und s := make([]int, 0)
  • \n
  • Wann man einen nil-Slice und wann einen leeren Slice verwendet
  • \n
  • Wie sich dies auf JSON, Speicher und Performance auswirkt
  • \n
  • Was die Go-Gurus und die Autoren der Standardbibliothek sagen
  • \n
\n\n

Zwei Zwillingsbrüder: nil slice vs empty slice

\n\n

In Go ist ein Slice eine Struktur aus drei Feldern: Zeiger auf ein Array, Länge und Kapazität. Wenn wir von einem leeren Slice sprechen, meinen wir einen Slice mit der Länge 0. Aber wie wir ihn erstellen, ist entscheidend.

\n\n

Methode 1: var-Deklaration (nil slice)

\n\n
var s []int  // s == nil, len(s) == 0, cap(s) == 0
\n\n

Dies ist ein nil-Slice. Er zeigt auf kein Array im Speicher. Dennoch können Sie bedenkenlos append darauf aufrufen – Go weist den Speicher automatisch zu.

\n\n

Methode 2: make-Initialisierung (empty slice)

\n\n
s := make([]int, 0)  // s != nil, len(s) == 0, cap(s) == 0
\n\n

Dies ist ein leerer Slice. Er zeigt bereits auf ein Null-Array (eine spezielle zerobase im Go-Runtime).

\n\n

Auf den ersten Blick ist der Unterschied nicht erkennbar. Aber lassen Sie uns tiefer graben.

\n\n

Konsolenausgabe und JSON – die erste Falle

\n\n

Versuchen Sie, diesen Code auszuführen:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "fmt"\n)\n\nfunc main() {\n    var nilSlice []int\n    emptySlice := make([]int, 0)\n\n    fmt.Printf("nilSlice: %v, is nil: %v\\n", nilSlice, nilSlice == nil)\n    fmt.Printf("emptySlice: %v, is nil: %v\\n", emptySlice, emptySlice == nil)\n\n    // JSON-Serialisierung\n    nilJSON, _ := json.Marshal(nilSlice)\n    emptyJSON, _ := json.Marshal(emptySlice)\n\n    fmt.Printf("nilSlice JSON: %s\\n", nilJSON)\n    fmt.Printf("emptySlice JSON: %s\\n", emptyJSON)\n}
\n\n

Das Ergebnis wird Sie überraschen:

\n\n
nilSlice: [], is nil: true\nemptySlice: [], is nil: false\nnilSlice JSON: null\nemptySlice JSON: []
\n\n

Hier ist die größte Überraschung! Die JSON-Serialisierung verhält sich unterschiedlich. Wenn Ihre API [] in der Antwort erwartet, Sie aber einen nil-Slice übergeben, erhält der Client null. Und das bricht Verträge.

\n\n

Viele REST-Clients (insbesondere in JavaScript) prüfen streng: Wenn in der Antwort ein Array erwartet wird, aber null ankommt, kann ein Fehler auftreten oder die Logik im Frontend gestört werden.

\n\n

Performance: Gibt es einen Unterschied?

\n\n

Die kurze Antwort: Ja, aber in 99% der Fälle ist er unbedeutend.

\n\n

Wenn Sie make([]int, 0) verwenden, weist Go sofort Speicher für die Slice-Struktur zu (24 Bytes auf einem 64-Bit-System) und verbindet sie mit der zerobase. Bei Verwendung von var erfolgt die Zuweisung erst beim ersten append.

\n\n

Wenn Sie einen Slice erstellen, der höchstwahrscheinlich leer bleibt, spart der nil-Slice eine Speicherzuweisung. Wenn Sie jedoch genau wissen, dass Sie Elemente hinzufügen werden, bietet make mit Angabe der Kapazität eine bessere Performance:

\n\n
// Wenn Sie die ungefähre Größe kennen, geben Sie die capacity an\ns := make([]int, 0, 100)  // Kapazität 100, Länge 0\nfor i := 0; i < 100; i++ {\n    s = append(s, i)  // ohne Neuzuweisungen!\n}
\n\n

Aber für die meisten Anwendungsfälle ist der Unterschied in der

Blogs

Buchempfehlungen