const ist in Go kein zweitklassiges var. Eine Konstante ist ein Compile-Time-Konzept: ihr Wert existiert nur während der Kompilierung, sie hat keine Speicheradresse, sie kann nicht überschrieben werden, und sie kennt — solange du sie nicht zwingst — keinen festen Typ. Genau diese Eigenschaft macht Konstanten flexibler als jede Variable: const Pi = 3.14159 passt sich an float32, float64 oder einen eigenen Typ type Angle float64 an, je nachdem, wo du sie verwendest. Dazu kommt iota — Gos eingebauter, blockweiser Index-Zähler, mit dem du Enum-Werte, Bitmasks und sequentielle Konstanten ohne Boilerplate baust. Dieser Artikel arbeitet beides Schicht für Schicht durch: Grammatik, untyped-vs-typed, Constant Expressions mit beliebiger Präzision, iota-Mechanik bis ins Detail, und die typischen Praxis-Pattern für Enums und Flags.
const-Grundlagen — Syntax und Block-Form
Die Grammatik aus der Go-Spec ist kompakt:
ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .Daraus ergeben sich drei praktische Schreibweisen:
package main
// (1) Einzelne Konstante, Typ wird abgeleitet
const MaxRetries = 5
const Greeting = "Hallo"
// (2) Konstante mit explizitem Typ
const Pi float64 = 3.14159265358979323846
const BufferSize int32 = 4096
// (3) Block-Form für mehrere Konstanten
const (
AppName = "mibeon"
AppVersion = "1.0.0"
MaxUsers = 10_000
)
// Mehrere Identifier in einer ConstSpec
const a, b, c = 3, 4, "foo" // a == 3, b == 4, c == "foo"
const u, v float32 = 0, 3 // u == 0.0, v == 3.0Die Block-Form ist auf Paket-Ebene idiomatisch: sie gruppiert verwandte Konstanten optisch und erlaubt — anders als bei var — eine Mechanik, die wir gleich detailliert sehen: die implizite Wiederholung des vorigen Ausdrucks, wenn die rechte Seite weggelassen wird. Das ist die Grundlage für alle iota-Pattern.
Die Anzahl der Identifier links muss zur Anzahl der Ausdrücke rechts passen. Ist ein Typ angegeben, gilt er für alle Konstanten der ConstSpec, und alle Ausdrücke müssen zu ihm zuweisbar sein.
Welche Werte sind konstanten-fähig
Nicht jeder Wert kann konstant sein. Die Spec definiert sechs Konstanten-Arten (constant kinds) — und nur Werte dieser Arten dürfen rechts vom = in einer const-Deklaration stehen:
| Art | Beispiele | Default-Typ |
|---|---|---|
| Boolean | true, false | bool |
| Rune | 'A', 'ä', '\n' | rune (= int32) |
| Integer | 42, 0xFF, 0b1010, 1_000_000 | int |
| Floating-Point | 3.14, 1e9, 0x1p-3 | float64 |
| Complex | 1+2i, 3i | complex128 |
| String | "hallo", `raw` | string |
Was nicht geht: Slices, Maps, Channels, Funktionen, Structs, Arrays — alles, was zur Laufzeit allokiert wird oder eine Identität jenseits seines Werts hat. Der Compiler verweigert mit const initializer ... is not a constant:
package main
import "time"
// OK — String- und Integer-Literale
const Name = "mibeon"
const Port = 8080
// FEHLER: alles unten ist kein Compile-Time-Wert
// const Buf = []byte{1, 2, 3} // Slice-Literal
// const M = map[string]int{"x": 1} // Map-Literal
// const Now = time.Now() // Funktionsaufruf
// const D = 5 * time.Second // OK! time.Second IST eine Konstante
// Konsequenz: Konstanten-„Defaults" für komplexe Typen
// müssen var-Deklarationen sein:
var DefaultTags = []string{"go", "docs"}
var _ = time.Second // damit der Import nicht ungenutzt isttime.Second funktioniert übrigens als Konstante, weil es im time-Paket selbst als const deklariert ist (time.Duration ist ein typisierter Integer, und Second = 1000 * Millisecond). Der scheinbare Methodenaufruf 5 * time.Second ist also eine reine Compile-Time-Multiplikation zweier Integer-Konstanten.
Untyped vs. Typed Constants — der entscheidende Unterschied
Hier liegt die Eleganz des Go-const-Systems. Eine Konstante kann untyped sein — sie hat dann zwar eine Art (Integer, Float, String, ...), aber noch keinen festen Typ. Erst wenn sie in einen typisierten Kontext kommt, wird sie zu diesem Typ konvertiert:
package main
import "fmt"
// untyped — passt sich an
const Pi = 3.14159265358979323846
// typed — fix als float64
const PiTyped float64 = 3.14159265358979323846
func main() {
var f32 float32 = Pi // OK: untyped Pi wird zu float32 konvertiert
var f64 float64 = Pi // OK: untyped Pi wird zu float64 konvertiert
// var bad float32 = PiTyped // FEHLER: cannot use PiTyped (float64) as float32
var ok float32 = float32(PiTyped) // OK mit explizitem Cast
fmt.Printf("f32=%v f64=%v ok=%v\n", f32, f64, ok)
}f32=3.1415927 f64=3.1415926535897932 ok=3.1415927Was hier passiert: Pi ist ein untyped floating-point constant. Solange sie nirgends einen Typ braucht, lebt sie als „idealer" Float weiter. Bei der Zuweisung an float32 wird sie innerhalb der Repräsentierbarkeit zu float32; bei float64 zu float64. PiTyped dagegen ist fest float64 — und Go's striktes Typsystem verbietet die implizite Konvertierung zu float32.
Daraus folgt eine Faustregel, die in der Standard-Library streng eingehalten wird: Konstanten ohne Typ deklarieren, außer du brauchst zwingend einen. Untyped ist mächtiger.
Eine untyped-Konstante bekommt erst dann einen Typ, wenn der Kontext einen erzwingt — und dann ihren Default-Typ (siehe Tabelle oben), falls keiner explizit verlangt wird:
package main
import "fmt"
const N = 42 // untyped integer
const X = 1.5 // untyped float
func main() {
i := N // i ist int (Default für untyped integer)
f := X // f ist float64 (Default für untyped float)
fmt.Printf("%T %T\n", i, f)
// In einem typisierten Kontext greift dieser Typ:
var u uint16 = N // N wird zu uint16
fmt.Printf("%T %v\n", u, u)
}int float64
uint16 42Constant Expressions — beliebige Präzision zur Compile-Zeit
Operationen zwischen Konstanten ergeben wieder Konstanten — und der Compiler arbeitet dabei mit beliebig hoher Präzision. Die Spec verpflichtet jede Implementierung auf mindestens 256 Bits Mantisse und 16 Bits Exponent für Float-Konstanten, in der Praxis ist es noch mehr. Es gibt kein Überlauf-Verhalten bei Konstanten:
package main
import "fmt"
// 2^100 ist als untyped integer-Konstante völlig legal —
// weit jenseits von int64 (max ~9.2 * 10^18).
const Big = 1 << 100
// Solange wir Big nur in weiteren Konstanten-Berechnungen verwenden,
// bleibt die Präzision erhalten:
const Half = Big / 2
const Third = Big / 3 // exakte Division im Integer-Ring (Rest verworfen)
func main() {
// Erst beim Übergang in eine Variable schlägt der Typ-Zwang zu.
// var x int = Big // FEHLER: constant 1267650... overflows int
// Aber als float64-Variable funktioniert es (mit Präzisionsverlust):
var f float64 = Big
fmt.Printf("%.0f\n", f)
// Auch das ist eine reine Compile-Time-Operation:
const Ratio = float64(Big) / float64(1<<99)
fmt.Println(Ratio)
}1267650600228229401496703205376
2Drei Beobachtungen, die hier wichtig sind:
1 << 100ist als Konstante legal, als Variable nicht. Der Compiler rechnet Konstanten symbolisch; erst beim Übergang in eine Variable wird auf den Ziel-Typ verkleinert. Passt der Wert dort nicht hinein, ist es ein Compile-Fehler — kein Runtime-Overflow.- Erlaubte Operanden für constant expressions sind ausschließlich andere Konstanten, dazu einige Builtins (
len,cap,min,max,unsafe.Sizeof,real,imag,complex), wenn sie auf Konstanten angewendet werden. - Die Mischung typisierter und untypisierter Konstanten in einem Ausdruck folgt einer einfachen Regel: ist ein typisierter Operand dabei, wird das ganze Ergebnis typisiert.
Die implizite Wiederholung — Voraussetzung für iota
Bevor wir iota selbst sehen, ein leicht zu übersehender Mechanismus, der iota erst nützlich macht. Die Spec sagt:
Within a parenthesized const declaration list the expression list may be omitted from any but the first ConstSpec. Such an empty list is equivalent to the textual substitution of the first preceding non-empty expression list and its type if any.
Heißt: Lässt du in einem const-Block die rechte Seite einer ConstSpec weg, wird die rechte Seite der vorigen nicht-leeren ConstSpec textuell wiederholt — inklusive Typ. Das ist eine reine Textmechanik, keine semantische Magie:
package main
import "fmt"
const (
A = 7
B // textuell: B = 7
C // textuell: C = 7
)
const (
Hello string = "hi"
World // textuell: World string = "hi"
)
func main() {
fmt.Println(A, B, C)
fmt.Println(Hello, World)
}7 7 7
hi hiFür sich genommen wirkt das nutzlos — drei Konstanten mit dem gleichen Wert. Sobald jedoch iota in der wiederholten Expression auftaucht, wird die Mechanik magisch: der gleiche Ausdruck wird wiederholt, aber iota hat in jeder ConstSpec einen anderen Wert.
iota — der eingebaute Index-Zähler
iota ist ein im Universe-Block predeclared Identifier — wie true, false, nil. Die exakte Spec-Definition:
Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants. Its value is the index of the respective ConstSpec in that constant declaration, starting at zero.
Drei Worte sind entscheidend: „successive", „index of the respective ConstSpec", „starting at zero". iota beginnt bei 0 in der ersten ConstSpec eines const-Blocks und ist in jeder weiteren ConstSpec um 1 höher. Es wird pro ConstSpec inkrementiert, nicht pro Identifier:
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1 (implizite Wiederholung: = iota)
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}0 1 2 3 4 5 6Was hier passiert, kombiniert beide vorherigen Mechaniken: Die erste ConstSpec setzt den Ausdruck iota. Die folgenden ConstSpecs lassen die rechte Seite weg — der Compiler wiederholt textuell iota, aber mit dem jeweils aktuellen Index. Ergebnis: eine sauber durchnummerierte Reihe ohne jede Wiederholung im Quelltext.
Wichtige Detail-Regeln:
- iota beginnt bei 0 in jedem neuen
const-Block. Zwei aufeinanderfolgendeconst-Blöcke haben jeder ihr eigenesiota, das jeweils bei 0 startet. - iota ist innerhalb einer ConstSpec konstant. Mehrfache Verwendungen von
iotain derselben Zeile haben denselben Wert. - iota wird auch dann inkrementiert, wenn es gar nicht benutzt wird. Eine ConstSpec wie
c = 3zählt mit, iota in der nächsten Zeile springt entsprechend. - iota ist untyped integer. Du kannst es in beliebige numerische Ausdrücke einbauen.
iota-Pattern — von Skip-Werten bis Bitmasks
Die echten Stärken von iota kommen mit Ausdrücken. Vier Pattern, die in Go-Code immer wieder auftauchen:
Pattern A — erster Wert überspringen mit _
Manchmal soll der nullte Wert eines Enums nicht als gültiger Member gelten — etwa weil 0 der Zero-Value des Typs ist und damit „nicht gesetzt" bedeutet. Skip mit dem Blank Identifier:
package main
import "fmt"
type Priority int
const (
_ Priority = iota // 0 — reserviert für „nicht gesetzt"
Low // 1
Medium // 2
High // 3
Critical // 4
)
func main() {
var p Priority // p == 0, also „nicht gesetzt"
fmt.Println(p, Low, Medium, High, Critical)
}0 1 2 3 4Pattern B — Bitmasks mit 1 << iota
Das wahrscheinlich häufigste iota-Pattern. Jeder Wert ist eine eigene Bit-Position; mit | werden Flags kombiniert, mit & getestet:
package main
import "fmt"
type Perm uint8
const (
PermExec Perm = 1 << iota // 1 << 0 == 1
PermWrite // 1 << 1 == 2
PermRead // 1 << 2 == 4
)
// Kombinierte Flags
const (
PermReadWrite = PermRead | PermWrite // 6
PermAll = PermRead | PermWrite | PermExec // 7
)
func (p Perm) Has(flag Perm) bool { return p&flag != 0 }
func main() {
user := PermReadWrite
fmt.Printf("can read? %v\n", user.Has(PermRead))
fmt.Printf("can exec? %v\n", user.Has(PermExec))
fmt.Printf("raw bits: %03b\n", user)
}can read? true
can exec? false
raw bits: 110Achtung: Bei 1 << iota ist die erste Konstante 1, nicht 0 — weil 1 << 0 == 1. Wer eine „Kein-Flag-gesetzt"-Sentinel will, schiebt eine _ Perm = iota-Zeile davor oder definiert separat const PermNone Perm = 0.
Pattern C — mehrere Konstanten pro ConstSpec
Da iota innerhalb einer ConstSpec konstant ist, kannst du es mehrfach in einer Zeile verwenden, um zusammengehörende Werte parallel zu erzeugen:
package main
import "fmt"
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // iota == 0: bit0=1, mask0=0
bit1, mask1 // iota == 1: bit1=2, mask1=1
_, _ // iota == 2: übersprungen
bit3, mask3 // iota == 3: bit3=8, mask3=7
)
func main() {
fmt.Println(bit0, mask0)
fmt.Println(bit1, mask1)
fmt.Println(bit3, mask3)
}1 0
2 1
8 7Beachte die dritte Zeile: _, _ zählt iota mit, ohne etwas zu binden. Das ist die Bestätigung der Spec-Regel — iota wird pro ConstSpec inkrementiert, völlig unabhängig davon, ob Identifier vergeben wurden.
Pattern D — Einheiten-Skala mit Bit-Shifts
Effective Gos berühmtes Beispiel für Datenmengen-Konstanten:
package main
import "fmt"
type ByteSize float64
const (
_ = iota // 0 ignorieren (B = 1 wäre verwirrend)
KB ByteSize = 1 << (10 * iota) // 1 << 10
MB // 1 << 20
GB // 1 << 30
TB // 1 << 40
PB // 1 << 50
EB // 1 << 60
)
func main() {
fmt.Printf("KB=%g\nMB=%g\nGB=%g\nTB=%g\n", KB, MB, GB, TB)
}KB=1024
MB=1.048576e+06
GB=1.073741824e+09
TB=1.099511627776e+12Hier zeigt sich auch die Stärke der arbiträren Präzision: 1 << 60 wäre als int Compiler-grenzwertig, als untyped-Konstante völlig unproblematisch. Erst die Zuweisung an ByteSize (= float64) löst den Wert konkret auf.
Praxis — wann iota passt, wann nicht
iota ist nicht der Hammer für jedes Konstanten-Schaubild. Vier Praxis-Fälle, jeweils mit der Entscheidung:
1. HTTP-Status-Codes — kein iota. Diese Werte sind extern definiert (RFC 9110: 200, 404, 500). Mit iota würdest du sie an die Quelltext-Reihenfolge koppeln und beim Einfügen eines neuen Codes alle nachfolgenden verschieben. Stattdessen explizit:
package httpx
const (
StatusOK = 200
StatusCreated = 201
StatusBadRequest = 400
StatusNotFound = 404
StatusInternalServerError = 500
)2. File-Permissions als Bitmask — iota glänzt. Jede Permission ist ein eigenes Bit, Reihenfolge ist egal, neue Permissions hängen sich hinten dran (siehe Pattern B oben).
3. State-Machine-Zustände — iota ideal. Geschlossene, lokal definierte Aufzählung mit kontinuierlich nummerierbaren Werten:
package main
import "fmt"
type ConnState int
const (
StateIdle ConnState = iota
StateDialing
StateConnected
StateClosing
StateClosed
)
func (s ConnState) String() string {
return [...]string{"Idle", "Dialing", "Connected", "Closing", "Closed"}[s]
}
func main() {
s := StateConnected
fmt.Println("state:", s)
}state: Connected4. Log-Level — iota in absteigender Severity oder aufsteigender Verbosity. Die Reihenfolge ist semantisch (Trace < Debug < Info < Warn < Error < Fatal), Vergleiche wie if level >= Warn werden mit iota natürlich:
package main
import "fmt"
type Level int
const (
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelFatal
)
func emit(min, current Level, msg string) {
if current >= min {
fmt.Printf("[%d] %s\n", current, msg)
}
}
func main() {
emit(LevelWarn, LevelInfo, "Info-Nachricht (unterdrückt)")
emit(LevelWarn, LevelError, "Fehler-Nachricht (durchgereicht)")
}[4] Fehler-Nachricht (durchgereicht)Die Faustregel: Eigene, geschlossene Aufzählung mit relevanter Reihenfolge → iota. Externe oder semantisch fixierte Werte → explizite Konstanten.
Typisierte Enums mit String()-Methode
iota gibt dir nur die Zahlen — sobald du Enums in Logs, Errors oder Tests siehst, willst du Klartext statt 3. Idiomatisch ist ein eigener Typ plus String()-Methode (fmt-Paket erkennt sie automatisch via fmt.Stringer-Interface):
package main
import "fmt"
// Eigener Typ — verhindert versehentliche Vermischung mit int.
type Color int
const (
Red Color = iota
Green
Blue
)
// Stringer-Interface: fmt ruft das automatisch auf.
func (c Color) String() string {
switch c {
case Red:
return "Red"
case Green:
return "Green"
case Blue:
return "Blue"
}
return fmt.Sprintf("Color(%d)", int(c))
}
func main() {
c := Green
fmt.Println("Farbe:", c) // ruft String() auf
fmt.Printf("Debug: %v / %d\n", c, c) // %v ruft String(), %d zeigt Zahl
}Farbe: Green
Debug: Green / 1Drei Punkte, die diesen Pattern ausmachen:
- Eigener Typ ist Pflicht für Typ-Sicherheit. Wäre
Coloreinfachint, könntest du jeden int-Wert übergeben — der Compiler würde nicht warnen. Mittype Color intakzeptiertfunc paint(c Color)nur Color-Werte (oder explizite Casts). fallbackim default-Zweig ist gute Hygiene: Falls jemandColor(99)aus einer DB liest, bekommst duColor(99)statt einer leeren Zeichenfolge.- Code-Generierung für
String()ist Standard. Das Toolstringerausgolang.org/x/tools/cmd/stringererzeugt die Switch-Funktion automatisch — du markierst die Datei mit einemgo:generate-Kommentar und rufstgo generate ./...auf:
package main
//go:generate stringer -type=Color
type Color int
const (
Red Color = iota
Green
Blue
)Nach go generate liegt color_string.go daneben mit einer fertigen, optimierten String()-Methode (Lookup über ein Index-Slice statt switch). Bei Enums mit vielen Werten ist das Pflicht-Tool.
Scope von const und iota
const-Identifier folgen denselben Scope-Regeln wie var — siehe Scoping-Regeln. Eine Konstante auf Paket-Ebene ist im ganzen Paket sichtbar; Großschreibung entscheidet über den Export:
package config
const (
// exportiert — andere Pakete sehen config.MaxConnections
MaxConnections = 100
DefaultPort = 8080
// package-private — kleingeschrieben
internalBuildTag = "dev"
)
func init() {
// Konstanten können auch funktions-lokal deklariert werden
const localTimeout = 30
_ = localTimeout
}iota dagegen hat einen engeren Scope: es ist nur innerhalb seines const-Blocks gültig. Außerhalb eines const ( ... ) ist iota nicht ansprechbar, und in einem neuen const-Block startet es wieder bei 0:
package main
import "fmt"
const (
A = iota // 0
B // 1
C // 2
)
const (
X = iota // 0 — neuer Block, iota wieder 0
Y // 1
)
// const Z = iota // legal, aber Z == 0 — eigene ConstSpec, eigener Block
func main() {
fmt.Println(A, B, C)
fmt.Println(X, Y)
}0 1 2
0 1Praktische Konsequenz: Wenn du eine durchgehende Nummerierung über mehrere logische Gruppen willst, müssen alle Konstanten im gleichen Block stehen. Trenne sie nicht in mehrere const ( ... )-Blöcke — sonst startet iota jedes Mal neu.
Häufige Stolperfallen
iota startet bei 0 — nicht bei 1.
Bei const ( A = iota; B; C ) ist A == 0. Wenn du 1, 2, 3 brauchst, schreib iota + 1 oder schiebe eine _ = iota-Zeile davor. Bei Enums ist das oft ein bewusster Trick: Der Zero-Value-Wert 0 markiert „nicht gesetzt", die echten Member starten ab 1.
iota wird pro ConstSpec inkrementiert, nicht pro Identifier.
In const ( a, b = iota, iota; c, d ) ist iota in der ersten Zeile überall 0 — also a=0, b=0. In der zweiten Zeile ist iota 1 — also c=1, d=1. Wer a=0, b=1, c=2, d=3 will, braucht vier separate ConstSpecs.
Typisierte Konstanten verlieren ihre Flexibilität.
const Pi float64 = 3.14159 lässt sich nicht direkt einer float32-Variable zuweisen — der Cast ist explizit nötig. const Pi = 3.14159 (untyped) passt sich automatisch an. Faustregel: Typ nur angeben, wenn du ihn wirklich brauchst.
Ohne String()-Methode wird das Enum als Zahl ausgegeben.
fmt.Println(Red) druckt 0, nicht Red, wenn Color keine String()-Methode hat. Spätestens für Log-Ausgaben oder Errors brauchst du sie. stringer aus golang.org/x/tools/cmd/stringer erzeugt sie automatisch via go generate.
Bei 1 << iota ist die erste Konstante 1, nicht 0.
Weil 1 << 0 == 1. Wer einen „Kein-Flag-gesetzt"-Sentinel braucht, schiebt vor die erste echte Bitmask eine Skip-Zeile (_ = iota) oder deklariert separat const FlagsNone Perm = 0. Achtung: ein Wert mit allen Bits gesetzt ist nicht iota-trivial — das berechnet man als FlagA | FlagB | ....
Leere ConstSpec wiederholt textuell die vorige Expression — mit aktueller iota.
Das ist die Mechanik hinter aller iota-Magie. const ( a = 1 << iota; b; c ) ist äquivalent zu const ( a = 1 << iota; b = 1 << iota; c = 1 << iota ) — wobei iota in jeder Zeile einen anderen Wert hat. Wichtig: Wiederholt wird der Ausdruck, nicht der berechnete Wert. Wer das verwechselt, baut subtile Bugs.
Operator-Reihenfolge in iota-Ausdrücken zählt.
1 << iota - 1 wird als 1 << (iota - 1) geparst — nicht als (1 << iota) - 1 — weil << niedrigere Präzedenz als - hat. Wer eine „alle Bits bis Position iota gesetzt"-Maske will, schreibt explizit (1 << iota) - 1 oder 1<<iota - 1 (Go-Lexer toleriert das ohne Whitespace ums <<). Bei Zweifeln: Klammern setzen.
Compile-Time-Überlauf bei Konstanten erkennt der Compiler.
const x int8 = 200 ist ein Compile-Fehler — int8 reicht von -128 bis 127. Genauso bei var x int8 = 200. Konstanten haben keinen Runtime-Overflow, sondern Compile-Time-Fehler — das ist eine ihrer Sicherheitsgarantien gegenüber Variablen mit dynamischen Werten.
iota ist auf den const-Block beschränkt — Verwendung außerhalb ist ein Fehler.
Du kannst iota nicht als Variable nutzen, nicht in var-Deklarationen, nicht in Funktionen. Es lebt nur innerhalb von const ( ... ). Praktische Konsequenz: durchgehende Nummerierung erfordert einen einzigen, ungeteilten Block.
Weiterführende Ressourcen
Externe Quellen
- Constant declarations – Go Language Specification
- Constants – Go Specification
- Iota – Go Specification
- Constants – The Go Blog (Rob Pike)
- Iota – Go Wiki
stringer– Code-Generator für Enum-Strings- Effective Go: Constants
Verwandte Artikel
- var vs. := – Variablen-Deklaration im Vergleich zu const
- Zero Values – warum 0 als „nicht gesetzt" oft praktisch ist
- Type Inference – wie Go den Typ aus dem Initialwert ableitet
- Scoping-Regeln – Sichtbarkeit von Konstanten und Variablen
- int und die Integer-Typen – Default-Typ für untyped integer-Konstanten