Menu

Golang Variablen: var, :=, Nullwerte und Shadowing

So deklarierst du Variablen in Go mit var und der Kurzform :=, was Nullwerte sind, Mehrfachzuweisung, und die Fehler durch Shadowing und ungenutzte Variablen, über die neue Go-Programmierer stolpern.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Go hat zwei Arten, eine Variable zu deklarieren: var, das überall funktioniert, und :=, eine Kurzform, die innerhalb von Funktionen funktioniert und den Typ ableitet.

Jede Variable hat einen festen Typ, der zur Compilezeit feststeht. Gibst du einen Wert an, leitet Go den Typ daraus ab. Den Typ zusätzlich hinzuschreiben (var city string = "Lisbon") ist erlaubt, aber überflüssig.

var vs :=

var:=
Woauf Paketebene und in Funktionennur in Funktionen
Typoptional; Pflicht, wenn es keinen Wert gibtimmer abgeleitet
Startwertoptional (sonst Nullwert)Pflicht
Neu deklarierennieerlaubt, wenn mindestens eine Variable links neu ist

Die idiomatische Wahl innerhalb einer Funktion:

  • x := value, wenn du einen Startwert hast.
  • var x T, wenn du den Nullwert willst. Das signalisiert „absichtlich leer“: var buf bytes.Buffer, var total int.
  • var x T = value, wenn der abgeleitete Typ nicht der gewünschte ist, zum Beispiel var ratio float64 = 1 (ein schlichtes ratio := 1 wäre ein int).

:= außerhalb einer Funktion ist ein Syntaxfehler:

./main.go:5:1: syntax error: non-declaration statement outside function body

Nullwerte

Eine Variable, die ohne Wert deklariert wird, enthält nie Datenmüll. Sie hat den Nullwert ihres Typs:

TypNullwert
Ganzzahlen, Floats0
boolfalse
string""
Pointer, Slices, Maps, Channels, Funktionen, Interfacesnil
Arrays, Structsjedes Element oder Feld auf seinen Nullwert gesetzt

Gute Go-APIs sind so entworfen, dass der Nullwert brauchbar ist: Ein sync.Mutex mit Nullwert ist entsperrt, ein bytes.Buffer mit Nullwert ist leer und einsatzbereit, und len, range und append funktionieren alle auf einem nil-Slice. Aufpassen musst du bei der Map: Aus einer nil-Map zu lesen liefert Nullwerte, in eine zu schreiben löst eine Panic aus. Erzeuge Maps mit make oder einem Literal, bevor du etwas hineinschreibst.

Mehrere Variablen deklarieren

Der Block var ( ... ) gruppiert zusammengehörige Variablen auf Paketebene. Bei einer Mehrfachzuweisung wie x, y = y, x wird jeder Ausdruck rechts ausgewertet, bevor irgendetwas zugewiesen wird. Deshalb funktioniert das Tauschen.

:= mit mehreren Namen ist erlaubt, solange mindestens ein Name links neu ist. Vorhandene Namen im selben Scope werden einfach zugewiesen. Deshalb kannst du err wiederverwenden:

f, err := os.Open("a.txt")   // declares f and err
g, err := os.Open("b.txt")   // declares g, assigns to the existing err

Ist keiner der Namen neu, gibt es einen Fehler:

./main.go:7:4: no new variables on left side of :=

Nimm ein schlichtes =, um einer bereits existierenden Variablen etwas zuzuweisen.

Ungenutzte Variablen sind Fehler

Eine lokale Variable, die deklariert, aber nie gelesen wird, stoppt den Build:

./main.go:9:2: declared and not used: x

Das ist keine Warnung, die du abschalten kannst. Lösch die Variable, benutz sie oder bring sie beim Debuggen mit dem Blank Identifier zum Schweigen: _ = x. Der Blank Identifier verwirft auch Rückgabewerte, die du nicht brauchst: _, err := fmt.Println("hi").

Variablen auf Paketebene und Funktionsparameter sind von dieser Regel ausgenommen.

Scope und Shadowing

Eine Variable existiert von ihrer Deklaration bis zum Ende des umschließenden Blocks ({ ... }). Ein := in einem inneren Block erzeugt eine neue Variable, auch wenn außen eine mit demselben Namen existiert. Die äußere wird verschattet, nicht geändert:

Das Programm gibt inside: 42 und dann outside: 0 aus. Das := im if hat sowohl count als auch err als neue Variablen im inneren Block deklariert, weil dort noch keine davon existierte. Es kompiliert sauber und ist eine klassische Quelle für Bugs der Sorte „mein Wert ist verschwunden“.

Die Lösung: err vorher deklarieren und mit = zuweisen:

var err error
count, err = strconv.Atoi(input)

go vet meldet Shadowing standardmäßig nicht. Der separate shadow-Analyzer tut es, und Linter wie golangci-lint können ihn aktivieren.

Benennung

  • Nimm camelCase: maxRetries, userID. Nicht max_retries.
  • Schreib Akronyme einheitlich: userID, httpClient, parseURL, nicht userId.
  • Kurze Namen für kurze Scopes: i in einer Schleife, r für einen Reader, err für einen Fehler. Längere, beschreibende Namen für Variablen auf Paketebene.
  • Ein großgeschriebener Name auf Paketebene (var Timeout) wird an andere Pakete exportiert. Siehe Pakete und Imports.

Variablen auf Paketebene

Außerhalb von Funktionen deklarierte Variablen leben während des ganzen Programms und werden vor dem Start von main in der Reihenfolge ihrer Abhängigkeiten initialisiert. Sie sind praktisch für Konfiguration und Caches, und alle Goroutinen teilen sie sich. Schreibt mehr als eine Goroutine darauf, schütze sie mit einem Mutex oder nimm sync/atomic, sonst hast du ein Data Race.

Für Werte, die sich nie ändern, nimm lieber Konstanten. Eine const kann nicht versehentlich geändert werden und kostet zur Laufzeit nichts.

Häufig gestellte Fragen

Was ist der Unterschied zwischen var und := in Go?

:= deklariert und initialisiert eine Variable mit abgeleitetem Typ und funktioniert nur innerhalb von Funktionen: count := 10. var funktioniert überall (auch auf Paketebene), erlaubt die Angabe des Typs und kann den Startwert weglassen, dann gilt der Nullwert: var count int. Innerhalb von Funktionen nutzt idiomatisches Go :=, wenn es einen Startwert gibt, und var, wenn du den Nullwert willst.

Was sind Nullwerte in Go?

Jede Variable, die ohne Wert deklariert wird, bekommt den Nullwert ihres Typs: 0 für Zahlen, false für bool, "" für string und nil für Pointer, Slices, Maps, Channels, Funktionen und Interfaces. Structs bekommen für jedes Feld dessen Nullwert. Nicht initialisierte Variablen gibt es in Go nicht.

Wie behebe ich „declared and not used“ in Go?

Go lehnt eine lokale Variable ab, die zugewiesen, aber nie gelesen wird. Benutz sie, lösch sie oder weise sie beim Debuggen mit _ = x dem Blank Identifier zu. Variablen auf Paketebene und Funktionsparameter sind ausgenommen.

Wie deklariere ich eine globale Variable in Go?

Mit var auf Paketebene, außerhalb jeder Funktion: var counter int. Die Kurzform := ist dort nicht erlaubt. Ein großgeschriebener Name (var Counter int) ist für andere Pakete sichtbar, ein kleingeschriebener nur im eigenen Paket. Schütze Variablen auf Paketebene mit einem Mutex, wenn Goroutinen sie schreiben.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S