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:

EBNF ConstDecl (Go-Spec)
ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

Daraus ergeben sich drei praktische Schreibweisen:

Go const_forms.go
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.0

Die 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:

ArtBeispieleDefault-Typ
Booleantrue, falsebool
Rune'A', 'ä', '\n'rune (= int32)
Integer42, 0xFF, 0b1010, 1_000_000int
Floating-Point3.14, 1e9, 0x1p-3float64
Complex1+2i, 3icomplex128
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:

Go const_invalid.go
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 ist

time.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:

Go untyped_vs_typed.go
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)
}
Output
f32=3.1415927 f64=3.1415926535897932 ok=3.1415927

Was 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:

Go default_type.go
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)
}
Output
int float64
uint16 42

Constant 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:

Go constexpr.go
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)
}
Output
1267650600228229401496703205376
2

Drei Beobachtungen, die hier wichtig sind:

  • 1 << 100 ist 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:

Go implicit_repeat.go
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)
}
Output
7 7 7
hi hi

Fü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:

Go iota_basic.go
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)
}
Output
0 1 2 3 4 5 6

Was 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 aufeinanderfolgende const-Blöcke haben jeder ihr eigenes iota, das jeweils bei 0 startet.
  • iota ist innerhalb einer ConstSpec konstant. Mehrfache Verwendungen von iota in derselben Zeile haben denselben Wert.
  • iota wird auch dann inkrementiert, wenn es gar nicht benutzt wird. Eine ConstSpec wie c = 3 zä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:

Go iota_skip.go
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)
}
Output
0 1 2 3 4

Pattern B — Bitmasks mit 1 << iota

Das wahrscheinlich häufigste iota-Pattern. Jeder Wert ist eine eigene Bit-Position; mit | werden Flags kombiniert, mit & getestet:

Go iota_bitmask.go
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)
}
Output
can read?  true
can exec?  false
raw bits:  110

Achtung: 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:

Go iota_parallel.go
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)
}
Output
1 0
2 1
8 7

Beachte 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:

Go iota_bytesize.go
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)
}
Output
KB=1024
MB=1.048576e+06
GB=1.073741824e+09
TB=1.099511627776e+12

Hier 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:

Go status_codes.go
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:

Go state_machine.go
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)
}
Output
state: Connected

4. 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:

Go log_levels.go
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)")
}
Output
[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):

Go color_enum.go
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
}
Output
Farbe: Green
Debug: Green / 1

Drei Punkte, die diesen Pattern ausmachen:

  • Eigener Typ ist Pflicht für Typ-Sicherheit. Wäre Color einfach int, könntest du jeden int-Wert übergeben — der Compiler würde nicht warnen. Mit type Color int akzeptiert func paint(c Color) nur Color-Werte (oder explizite Casts).
  • fallback im default-Zweig ist gute Hygiene: Falls jemand Color(99) aus einer DB liest, bekommst du Color(99) statt einer leeren Zeichenfolge.
  • Code-Generierung für String() ist Standard. Das Tool stringer aus golang.org/x/tools/cmd/stringer erzeugt die Switch-Funktion automatisch — du markierst die Datei mit einem go:generate-Kommentar und rufst go generate ./... auf:
Go generated_stringer.go
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:

Go const_scope.go
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:

Go iota_resets.go
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)
}
Output
0 1 2
0 1

Praktische 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

/ Weiter

Zurück zu Variablen & Konstanten

Zur Übersicht