Menu

Stałe w Go (Golang const): stałe typowane i beztypowe

Jak działa const w Go: deklarowanie stałych, różnica między stałymi typowanymi i beztypowymi, wyrażenia stałe o dowolnej precyzji oraz dlaczego Go nie ma stałych slice'ów, map ani struktur.

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

Stała to wartość ustalona w czasie kompilacji. Deklarujesz ją przez const, a Go odrzuca każdą próbę jej zmiany.

Stałe można deklarować na poziomie pakietu lub wewnątrz funkcji, po jednej w linii albo zgrupowane w bloku const ( ... ). MB i GB są obliczane z innych stałych, a kompilator wykonuje te obliczenia raz, podczas budowania.

Przypisanie do stałej, na przykład MaxRetries = 5, to błąd kompilacji:

./main.go:8:2: cannot assign to MaxRetries (neither addressable nor a map index expression)

Co może być stałą

Tylko trzy rodzaje wartości: wartości logiczne, liczby (całkowite, zmiennoprzecinkowe, zespolone i runy) oraz stringi. Wartość musi dać się obliczyć przez kompilator, czyli mogą to być literały, inne stałe, działania na nich i kilka funkcji wbudowanych, jak len ze stałego stringa.

Wszystko, co wymaga uruchomienia kodu lub alokacji pamięci, odpada:

const Colors = []string{"red", "green"} // slice
const Started = time.Now()              // function call
./main.go:9:16: []string{…} (value of type []string) is not constant
./main.go:10:13: time.Now() (value of struct type time.Time) is not constant

Dlaczego Go nie ma stałych slice'ów ani map

Slice'y, mapy i struktury, które je zawierają, to referencje do pamięci istniejącej w czasie działania, więc kompilator nie może wbudować ich w program jako stałych wartości, a Go nie ma modyfikatora readonly ani final dla zmiennych. Typowe obejścia:

Kopia kosztuje jedną alokację na wywołanie, co jest w porządku przy danych wielkości konfiguracji. Do wyszukiwania po kluczu inną opcją przypominającą stałą jest switch w funkcji: func statusText(code int) string { switch code { ... } }.

Stałe beztypowe

Stała zadeklarowana bez typu jest beztypowa (untyped). Ma rodzaj (liczba całkowita, zmiennoprzecinkowa, runa, string, bool), ale nie ma konkretnego typu Go, dopóki nie zostanie użyta, i dopasowuje się do kontekstu:

Ratio działa jako int, float64, uint8 i float32 bez konwersji. Dlatego time.Sleep(2 * time.Second) się kompiluje: 2 jest beztypowe i staje się time.Duration.

Gdy kontekst nie wymaga typu, jak w x := Ratio, stała dostaje swój typ domyślny:

Rodzaj stałej beztypowejTyp domyślny
całkowita (42)int
zmiennoprzecinkowa (4.2)float64
runa ('a')rune (int32)
zespolona (2i)complex128
stringstring
logicznabool

Dokładna arytmetyka

Beztypowe stałe liczbowe są dokładne. Kompilator reprezentuje liczby całkowite na co najmniej 256 bitach, więc wartości pośrednie mogą być dużo większe niż jakikolwiek typ Go:

Samej Huge nie da się wypisać jako liczby całkowitej, bo przekazanie jej do fmt.Println konwertuje ją na int, a kompilator to wyłapuje (float64(Huge) działa, bo float pomieści taką wielkość):

cannot use Huge (untyped int constant 1267650600228229401496703205376) as int value in argument to fmt.Println (overflows)

Ta sama kontrola zatrzymuje w czasie kompilacji mniejsze pomyłki, jak var b byte = 300:

cannot use 300 (untyped int constant) as byte value in variable declaration (overflows)

Stałe typowane

Nadaj stałej typ, a wszędzie zachowa się jak wartość tego typu, łącznie z tym, że Go odmówi mieszania typów:

const Limit int = 10

var f float64 = 2
fmt.Println(Limit * f)
invalid operation: Limit * f (mismatched types int and float64)

Z const Limit = 10 (beztypową) Limit * f się kompiluje i daje 20. Dlatego zostawiaj stałe beztypowe, chyba że typ niesie znaczenie. Niesie je wtedy, gdy deklarujesz stałe własnego nazwanego typu, bo tak Go buduje enumy:

type Weekday int

const (
	Sunday Weekday = iota
	Monday
	Tuesday
)

Tutaj typ jest sednem: funkcja przyjmująca Weekday dokumentuje, czego oczekuje. iota i oparte na nim wzorce enumów opisuje strona o enumach i iota.

const a var

constvar
Może się zmienićnietak
Wartość znanaw czasie kompilacjiw czasie działania
Dozwolone typybool, liczbowe, stringdowolne
Zajmuje pamięć w czasie działanianietak
Można pobrać adres (&x)nietak
Nieużyta to błądnietak, dla zmiennych lokalnych

Używaj const do wszystkiego, co jest naprawdę stałe: limitów, rozmiarów, nazw, kodów protokołów, formatów tekstu. Kontrolę przepełnienia w czasie kompilacji i dokładną arytmetykę dostajesz za darmo.

Pułapki

Stałe typowane się nie dopasowują. const Timeout int = 5 nie przekażesz tam, gdzie oczekiwany jest time.Duration, bez konwersji; beztypową const Timeout = 5 można pomnożyć: Timeout * time.Second.

Na stałych całkowitych dzielenie jest całkowite. const Half = 1 / 2 to 0, bo oba argumenty są beztypowymi liczbami całkowitymi. Napisz 1.0 / 2, żeby dostać 0.5.

Brak adresu. &MaxRetries się nie kompiluje. Jeśli API chce *int, najpierw skopiuj stałą do zmiennej.

Najczęściej zadawane pytania

Jak zadeklarować stałą w Go?

Za pomocą const: const MaxRetries = 3 albo z jawnym typem const Timeout time.Duration = 5 * time.Second. Kilka stałych można zgrupować w bloku: const ( A = 1; B = 2 ). Wartość musi dać się obliczyć w czasie kompilacji.

Czy w Go można zrobić stały slice, mapę lub tablicę?

Nie. Stałe mogą być tylko wartościami logicznymi, liczbami (w tym runami) i stringami. const Colors = []string{"red"} kończy się błędem is not constant. Użyj var na poziomie pakietu, a jeśli wywołujący nie mogą go zmieniać, udostępnij funkcję zwracającą świeżą kopię.

Czym jest stała beztypowa (untyped) w Go?

To stała zadeklarowana bez typu, na przykład const Pi = 3.14159. Nie ma ustalonego typu, dopóki nie zostanie użyta, więc ta sama stała działa jako float32, float64 albo, jeśli wartość na to pozwala, int. Stałe beztypowe są też dokładne: kompilator trzyma co najmniej 256 bitów precyzji, więc const Big = 1 << 100 jest poprawne, o ile używasz jej tylko w wyrażeniach, których wynik się mieści.

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

const jest ustalane w czasie kompilacji i nigdy się nie zmienia; nie zajmuje pamięci w czasie działania programu i może przechowywać tylko bool, liczbę lub string. var to miejsce w pamięci, któremu można przypisać nową wartość, może mieć dowolny typ i przechowywać wartości obliczane w czasie działania, jak time.Now().

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ