====== Enums — Aufzählungstypen ======
Ein ''enum'' definiert einen Typ, dessen gültige Werte eine abgeschlossene, benannte Menge bilden. Der Compiler kennt alle Werte zur Compile-Zeit — das ist die Grundlage der Exhaustivitätsprüfung in ''match'': Wird ein Enum-Wert nicht behandelt, ist es ein **Fehler**, keine Warnung.
Enums sind in Lyx ganzzahlige Typen (intern ''int64'') mit benannten Konstanten. Sie haben keine Methoden und keine Vererbung — ihr einziger Zweck ist die typsichere Zustandsdarstellung.
→ [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching]] · [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] · [[lyx_-_programmiersprache:guides:do-178c|DO-178C]]
----
===== 1. Deklaration =====
// Einfachster Fall: Werte beginnen bei 0 und zählen aufwärts
enum Direction { North, South, East, West }
// North=0, South=1, East=2, West=3
// Explizite Werte — ganze Zahlen im Bereich 0..4294967295
enum HttpStatus {
Ok = 200,
Created = 201,
NotFound = 404,
ServerErr = 500
}
// Gemischt: erste Konstante setzt den Startwert, die folgenden zählen weiter
enum Priority {
Low = 1,
Medium, // = 2
High, // = 3
Critical // = 4
}
Regeln:
* Der Wertebereich ist ''0..4294967295'' (uint32). Negative Werte und alles darüber werden abgewiesen: ''enum E: der Wert von A liegt ausserhalb von 0..4294967295''. Ein Enum trägt also **keine** beliebigen int64-Werte.
* Gleiche Namen in **verschiedenen** Enums sind unproblematisch — der Qualifier trennt sie (''E1.X'' und ''E2.X'').
* Enums dürfen leer sein (''enum Empty {}'' — selten sinnvoll, aber erlaubt).
* ''%%enum E { A, A }%%'' — derselbe Name doppelt — wird abgewiesen.
* ''%%enum E { A = 1, B = 1 }%%'' — zwei Namen auf denselben Wert — ist dagegen erlaubt: als Alias ist das oft gewollt.
----
===== 2. Verwendung =====
Ein einfaches Anwendungsbeispiel:
enum Color { Red, Green, Blue }
// Variablendeklaration
var c: Color := Color.Red;
// Übergabe als Parameter
fn PrintColor(col: Color): void {
match (col) {
case Color.Red => Print("Rot\n");
case Color.Green => Print("Grün\n");
case Color.Blue => Print("Blau\n");
}
}
// Rückgabe aus Funktion
fn OppositeDirection(d: Direction): Direction {
return match (d) {
case Direction.North => Direction.South;
case Direction.South => Direction.North;
case Direction.East => Direction.West;
case Direction.West => Direction.East;
};
}
Der Zugriff auf einen Enum-Wert erfolgt immer mit ''EnumName.Wert'' — der Namespace-Qualifier ist Pflicht. Das verhindert Namenskollisionen zwischen verschiedenen Enums.
Ein Tippfehler im Member-Namen ist ein Compile-Fehler: ''Color.Rrd'' wird abgewiesen, nicht stillschweigend zu ''0''.
----
===== 3. Enums und match =====
Die Stärke von Enums zeigt sich in Kombination mit ''match'': Der Compiler erzwingt, dass **jeder** mögliche Wert behandelt wird.
enum FlightPhase {
Preflight, Taxiing, Takeoff, Climbing,
Cruise, Descending, Approach, Landing, Rollout
}
fn PhaseToString(p: FlightPhase): pchar {
return match (p) {
case FlightPhase.Preflight => "Vor dem Start";
case FlightPhase.Taxiing => "Rollen";
case FlightPhase.Takeoff => "Start";
case FlightPhase.Climbing => "Steigflug";
case FlightPhase.Cruise => "Reiseflug";
case FlightPhase.Descending => "Sinkflug";
case FlightPhase.Approach => "Anflug";
case FlightPhase.Landing => "Landung";
case FlightPhase.Rollout => "Ausrollen";
};
}
Wird später ''FlightPhase.GoAround'' hinzugefügt, meldet der Compiler:
sema error (line 3): match does not cover variant 'GoAround' — add a case or a wildcard _ => { ... }
Das ist eine **Sicherheitsgarantie**: Neue Zustände können nicht unbehandelt durchrutschen.
==== Wann default erlaubt ist ====
''default'' unterdrückt die Exhaustivitätsprüfung. Das ist explizit erlaubt, aber sparsam einsetzen:
fn IsErrorStatus(s: HttpStatus): bool {
return match (s) {
case HttpStatus.ServerErr => true;
case _ => false;
// Explizite Entscheidung: alle nicht genannten Werte sind "kein Fehler"
// Risiko: neuer Wert wie HttpStatus.GatewayTimeout wird stillschweigend false
};
}
> In DO-178C DAL-A/B-Code ist ''default'' bei zustandsbehafteten Enums ein Compiler-Fehler wenn ''@dal(A)'' oder ''@dal(B)'' gesetzt ist. Alle Zustände müssen explizit behandelt sein.
----
===== 4. Enum als Integer =====
Intern ist jeder Enum-Wert ein ''int64''. Die explizite Konvertierung erfolgt mit ''as'':
enum HttpStatus { Ok = 200, NotFound = 404, ServerErr = 500 }
// Enum → int64
var code: int64 := HttpStatus.Ok as int64; // 200
// int64 → Enum (unsafe: der Compiler prüft den Wert nicht)
var s: HttpStatus := 404 as HttpStatus;
// In der Praxis: Wert aus Netzwerk empfangen, in Enum konvertieren
fn ParseStatus(raw: int64): HttpStatus {
return match (raw) {
case 200 => HttpStatus.Ok;
case 404 => HttpStatus.NotFound;
case 500 => HttpStatus.ServerErr;
case _ => HttpStatus.ServerErr; // Fallback bei unbekanntem Code
};
}
> Die Konvertierung ''intVal as HttpStatus'' ist technisch möglich, aber gefährlich: Der Wert ''999'' wäre ein ungültiger ''HttpStatus''. In ''@dal(A)''-Code ist der direkte Cast von ''int64'' zu einem Enum verboten — stattdessen muss ''ParseStatus'' oder eine vergleichbare Funktion mit explizitem ''match'' verwendet werden.
----
===== 5. Bit-Flag-Enums =====
Für Bit-Masken werden Enums mit Zweierpotenzen als Werte deklariert und mit Integer-Operatoren kombiniert:
enum Permission {
Read = 0x01, // Bit 0
Write = 0x02, // Bit 1
Execute = 0x04, // Bit 2
Admin = 0x08 // Bit 3
}
// Mehrere Flags kombinieren (als int64 — nicht als Permission)
var perms: int64 := Permission.Read as int64 | Permission.Write as int64;
// Flag prüfen
fn HasPermission(perms: int64, flag: Permission): bool {
return (perms & (flag as int64)) != 0;
}
fn main(): int64 {
var p: int64 := Permission.Read as int64 | Permission.Execute as int64;
if (HasPermission(p, Permission.Write)) {
PrintLn("Schreiben erlaubt");
} else {
Print("Kein Schreibzugriff\n"); // Dieser Zweig wird ausgeführt
}
return 0;
}
Bit-Flag-Enums werden immer als ''int64'' kombiniert — Lyx bietet keinen speziellen Bitset-Typ. Das ist ein bewusstes Design: Bitoperationen sind explizit und direkt im Code sichtbar.
==== Masken als Konstante ====
Seit **1.0.20A** gelten Enum-Werte auch in ''con''-Ausdrücken und in Startwerten globaler Variablen (#1507). Damit lässt sich eine Maske einmal benennen, statt sie an jeder Prüfstelle neu zusammenzusetzen:
enum P { Read = 1, Write = 2, Exec = 4 }
con MASK: int64 := P.Read as int64 | P.Write as int64;
var perms: int64 := P.Read as int64;
fn main(): int64 {
PrintLn("MASK=", IntToStr(MASK), " perms=", IntToStr(perms));
return 0;
}
MASK=3 perms=1
Die **Vorwärtsreferenz** ist zulässig — das ''enum'' darf unter der ''con''-Zeile stehen, die es benutzt.
**Vor 1.0.20A ergab genau dieses Muster still ''0''.** Die ''con''-Zeile übersetzte ohne Warnung, ''MASK'' war aber 0 — und damit jede Prüfung ''(wert & MASK) != 0'' immer falsch. Wer eine ältere Fassung benutzt, prüft seine Masken nach; der Fehler ist am Quelltext nicht zu sehen.
----
===== 6. Enums in Datenstrukturen =====
Enums als Felder in Structs und als Array-Elemente:
import std.alloc;
enum State { Idle, Running, Paused, Stopped }
// Enum-Feld in einem alloc'd Puffer (Offset-Muster)
pub con TASK_SIZE: int64 := 24;
pub con TASK_STATE: int64 := 0; // State als int64 gespeichert
pub con TASK_ID: int64 := 8;
pub con TASK_PRIO: int64 := 16;
fn TaskCreate(id: int64, prio: int64): int64 {
var t: int64 := alloc(TASK_SIZE);
if (t == 0) { return 0; }
poke64(t + TASK_STATE, State.Idle as int64);
poke64(t + TASK_ID, id);
poke64(t + TASK_PRIO, prio);
return t;
}
fn TaskGetState(t: int64): State {
return peek64(t + TASK_STATE) as State;
}
fn TaskSetState(t: int64, s: State): void {
poke64(t + TASK_STATE, s as int64);
}
fn TaskIsRunning(t: int64): bool {
return TaskGetState(t) == State.Running;
}
----
===== 7. Enums in Safety-Code =====
Enums sind ein bevorzugtes Mittel in sicherheitskritischem Code, weil sie den Zustandsraum explizit und abgeschlossen definieren:
@flight_crit(DAL-A)
enum EngineState {
Off = 0,
Starting = 1,
Idle = 2,
Running = 3,
Overspeed = 4,
Shutdown = 5
}
@flight_crit(DAL-A)
fn EngineTransition(current: EngineState, cmd: EngineCommand): EngineState {
// Vollständige Zustandsmaschine — kein default, alle Kombinationen explizit
return match (current) {
case EngineState.Off => match (cmd) { ... };
case EngineState.Starting => match (cmd) { ... };
case EngineState.Idle => match (cmd) { ... };
case EngineState.Running => match (cmd) { ... };
case EngineState.Overspeed => EngineState.Shutdown; // immer herunterfahren
case EngineState.Shutdown => EngineState.Off;
};
}
^ Eigenschaft ^ Enum ^ int64-Konstanten ^
| Compiler-Exhaustivitätsprüfung | ✅ Ja | ❌ Nein |
| Typsicherheit im Parameter | ✅ Ja | ❌ Nein (jede int64 passt) |
| Dokumentation im Code | ✅ Explizite Namen | Eingeschränkt |
| Gültigkeitsbereich | Pro Enum getrennt | Global |
| DO-178C geeignet | ✅ Empfohlen für Zustände | Bedingt |
----
===== 8. Zusammenfassung =====
^ Frage ^ Antwort ^
| Basistyp | ''int64'' (immer) |
| Zugriffssyntax | ''EnumName.Wert'' |
| Methoden | Nicht unterstützt |
| Enum → int64 | ''val as int64'' |
| int64 → Enum | ''intVal as EnumTyp'' (unsafe — Wert unklar) |
| match-Exhaustivität | Pflicht ohne ''default'' |
| In Safety-Code | Enums für alle Zustände bevorzugen, kein ''default'' |
| Bit-Flags | Werte als Zweierpotenzen; kombinieren als ''int64'' |
→ [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching — match, Guards, Exhaustivität]]\\
→ [[lyx_-_programmiersprache:sprache:datentypen|Datentypen — vollständige Typübersicht]]\\
→ [[lyx_-_programmiersprache:sprache:typaliase-und-typumwandlung|Typ-Aliase und Typumwandlung (as, is)]]
Letzte Aktualisierung: 2026-08-09
Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).