Menu

Zmienne w Golang: var, :=, wartości zerowe i przesłanianie

Jak deklarować zmienne w Go przez var i krótką formę :=, czym są wartości zerowe, przypisanie wielokrotne oraz błędy przesłaniania i nieużywanych zmiennych, które zaskakują początkujących w Go.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Go ma dwa sposoby deklarowania zmiennej: var, który działa wszędzie, i :=, krótką formę, która działa wewnątrz funkcji i wnioskuje typ.

Każda zmienna ma stały typ ustalony podczas kompilacji. Gdy podajesz wartość, Go wnioskuje z niej typ, więc dopisanie typu (var city string = "Lisbon") jest poprawne, ale zbędne.

var a :=

var:=
Gdziena poziomie pakietu i wewnątrz funkcjitylko wewnątrz funkcji
Typopcjonalny; wymagany, gdy nie ma wartościzawsze wnioskowany
Wartość początkowaopcjonalna (w przeciwnym razie wartość zerowa)wymagana
Ponowna deklaracjanigdydozwolona, jeśli co najmniej jedna zmienna po lewej jest nowa

Idiomatyczny wybór wewnątrz funkcji:

  • x := value, gdy masz wartość początkową.
  • var x T, gdy chcesz wartość zerową, co sygnalizuje „celowo puste”: var buf bytes.Buffer, var total int.
  • var x T = value, gdy wywnioskowany typ nie jest tym, którego chcesz, na przykład var ratio float64 = 1 (samo ratio := 1 dałoby int).

:= poza funkcją to błąd składni:

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

Wartości zerowe

Zmienna zadeklarowana bez wartości nigdy nie zawiera śmieci. Przechowuje wartość zerową swojego typu:

TypWartość zerowa
liczby całkowite, zmiennoprzecinkowe0
boolfalse
string""
wskaźniki, slice'y, mapy, kanały, funkcje, interfejsynil
tablice, strukturykażdy element lub pole ustawione na wartość zerową

Dobre API w Go są projektowane tak, by wartość zerowa była użyteczna: zerowy sync.Mutex jest odblokowany, zerowy bytes.Buffer jest pusty i gotowy, a len, range i append działają na slice'ie nil. Uważaj na mapy: odczyt z mapy nil zwraca wartości zerowe, ale zapis do niej wywołuje panic. Twórz mapy przez make lub literał, zanim coś w nich zapiszesz.

Deklarowanie kilku zmiennych

Blok var ( ... ) grupuje powiązane zmienne na poziomie pakietu. W przypisaniu wielokrotnym, takim jak x, y = y, x, każde wyrażenie po prawej jest obliczane przed jakimkolwiek przypisaniem, dlatego zamiana działa.

:= z kilkoma nazwami niczego nie deklaruje ponownie, dopóki co najmniej jedna nazwa po lewej jest nowa. Istniejące nazwy w tym samym zasięgu są po prostu przypisywane. Dzięki temu możesz ponownie używać err:

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

Jeśli żadna z nazw nie jest nowa, to błąd:

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

Użyj zwykłego =, aby przypisać wartość do zmiennej, która już istnieje.

Nieużywane zmienne to błędy

Zmienna lokalna, która została zadeklarowana, ale nigdy nie została odczytana, zatrzymuje kompilację:

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

To nie jest ostrzeżenie, które można wyłączyć. Usuń zmienną, użyj jej albo podczas debugowania wycisz ją pustym identyfikatorem: _ = x. Pusty identyfikator odrzuca też niepotrzebne wartości zwracane: _, err := fmt.Println("hi").

Zmienne na poziomie pakietu i parametry funkcji są z tej zasady zwolnione.

Zasięg i przesłanianie

Zmienna istnieje od deklaracji do końca otaczającego bloku ({ ... }). := w bloku wewnętrznym tworzy nową zmienną, nawet jeśli na zewnątrz istnieje zmienna o tej samej nazwie. Zewnętrzna zostaje przesłonięta, a nie zmieniona:

Program wypisuje inside: 42, a potem outside: 0. := wewnątrz if zadeklarowało zarówno count, jak i err jako nowe zmienne w bloku wewnętrznym, bo żadna z nich jeszcze tam nie istniała. Kod kompiluje się bez problemu i jest klasycznym źródłem błędów typu „moja wartość zniknęła”.

Rozwiązaniem jest najpierw zadeklarować err i przypisywać przez =:

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

go vet domyślnie nie zgłasza przesłaniania. Robi to osobny analizator shadow, a lintery takie jak golangci-lint mogą go włączyć.

Nazewnictwo

  • Używaj camelCase: maxRetries, userID. Nie max_retries.
  • Akronimy zapisuj jednolicie: userID, httpClient, parseURL, a nie userId.
  • Krótkie nazwy dla krótkich zasięgów: i w pętli, r dla readera, err dla błędu. Dłuższe, opisowe nazwy dla zmiennych na poziomie pakietu.
  • Nazwa z wielkiej litery na poziomie pakietu (var Timeout) jest eksportowana do innych pakietów. Zobacz stronę o pakietach i importach.

Zmienne na poziomie pakietu

Zmienne zadeklarowane poza funkcjami żyją przez cały czas działania programu i są inicjalizowane przed uruchomieniem main, w kolejności zależności. Są wygodne do konfiguracji i cache, a współdzieli je każda goroutine. Jeśli do jednej z nich zapisuje więcej niż jedna goroutine, chroń ją mutexem albo użyj sync/atomic, bo inaczej masz wyścig danych.

Dla wartości, które nigdy się nie zmieniają, wybieraj stałe. const nie da się przypadkiem zmodyfikować i nic nie kosztuje w czasie działania.

Najczęściej zadawane pytania

Czym różni się var od := w Go?

:= deklaruje i inicjalizuje zmienną z wywnioskowanym typem i działa tylko wewnątrz funkcji: count := 10. var działa wszędzie (także na poziomie pakietu), pozwala podać typ i może pominąć wartość początkową, żeby dostać wartość zerową: var count int. Wewnątrz funkcji idiomatyczne Go używa :=, gdy jest wartość początkowa, i var, gdy chcesz wartość zerową.

Czym są wartości zerowe w Go?

Każda zmienna zadeklarowana bez wartości dostaje wartość zerową swojego typu: 0 dla liczb, false dla bool, "" dla string i nil dla wskaźników, slice'ów, map, kanałów, funkcji i interfejsów. Struktury dostają wartość zerową każdego pola. Go nie ma niezainicjalizowanych zmiennych.

Jak naprawić błąd "declared and not used" w Go?

Go odrzuca zmienną lokalną, do której coś przypisano, ale której nigdy nie odczytano. Użyj jej, usuń ją albo podczas debugowania przypisz ją do pustego identyfikatora przez _ = x. Zmienne na poziomie pakietu i parametry funkcji są z tej zasady zwolnione.

Jak zadeklarować zmienną globalną w Go?

Użyj var na poziomie pakietu, poza jakąkolwiek funkcją: var counter int. Krótka forma := jest tam niedozwolona. Nazwa z wielkiej litery (var Counter int) jest widoczna dla innych pakietów; z małej litery tylko we własnym pakiecie. Chroń zmienne na poziomie pakietu mutexem, jeśli zapisują do nich goroutine.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ