====== Typ-Aliase und Typumwandlung ======
Lyx hat ein **explizites** Typsystem: Implizite Konvertierungen gibt es nicht. Jede Typumwandlung muss mit dem ''as''-Operator geschrieben werden. Typ-Aliase über ''type'' benennen vorhandene Typen neu — entweder als einfaches Alias, als Range-Typ mit Wertebereichseinschränkung oder als neuen Struct-/Class-Typ.
→ [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] · [[lyx_-_programmiersprache:sprache:enums|Enums]] · [[lyx_-_programmiersprache:sprache:oop|OOP — Klassen und Vererbung]]
----
===== 1. Typ-Aliase mit type =====
''type'' deklariert einen neuen Namen für einen vorhandenen Typ oder definiert einen zusammengesetzten Typ:
==== Einfacher Alias für Primitivtypen ====
// Einfache Aliase — kein neuer Typ, nur ein anderer Name
type Byte = uint8;
type Word = uint16;
type DWord = uint32;
type QWord = int64;
type Fd = int64; // Dateideskriptor
type Handle = int64; // Generischer Ressource-Handle
type Ptr = int64; // Roher Zeiger als int64
// Verwendung: exakt wie der Basistyp
var fd: Fd := open("/etc/hosts", 0, 0);
var h: Handle := SQLiteOpen("/data/app.db");
Einfache Aliase sind **strukturell äquivalent** zum Basistyp: Eine Funktion, die ''int64'' erwartet, akzeptiert auch ''Fd'' — der Compiler betrachtet sie als denselben Typ.
==== Aliase für Lesbarkeit ====
type Celsius = f64;
type Kelvin = f64;
type Millibar = f64;
fn CelsiusToKelvin(temp: Celsius): Kelvin {
return temp + 273.15;
}
fn main(): int64 {
var t: Celsius := 20.0;
var k: Kelvin := CelsiusToKelvin(t);
// Hinweis: Celsius und Kelvin sind beide f64 — der Compiler unterscheidet sie NICHT
// Für echte Typsicherheit → Range-Typen (Abschnitt 2)
PrintF64(k);
return 0;
}
==== Aliase für Structs und Klassen ====
Die häufigste Form von ''type'': Neue Struct- und Klassentypen (ausführlich in [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] und [[lyx_-_programmiersprache:sprache:oop|OOP]] behandelt):
type Point = struct {
x: f64;
y: f64;
fn Distance(other: Point): f64 {
var dx: f64 := self.x - other.x;
var dy: f64 := self.y - other.y;
return Sqrt(dx * dx + dy * dy);
}
};
type Vector2 = struct { x: f64; y: f64; }; // Kompaktform ohne Methoden
----
===== 2. Range-Typen =====
Range-Typen schränken den gültigen Wertebereich eines Integer-Typs ein. Der Compiler prüft Literale zur Compile-Zeit; Laufzeit-Verstöße erzeugen einen Panic.
type Altitude = int64 range -1000..60000; // Meter über NN
type Speed = int64 range 0..300; // km/h
type DalLevel = int8 range 1..5;
type Temp = int32 range -273..1000; // Grad Celsius
type Percent = uint8 range 0..100;
type Port = uint16 range 1..65535;
==== Compile-Zeit-Prüfung ====
var alt: Altitude := 70000; // ✗ Compiler-Fehler: 70000 liegt außerhalb -1000..60000
var spd: Speed := -10; // ✗ Compiler-Fehler: -10 liegt außerhalb 0..300
var pct: Percent := 101; // ✗ Compiler-Fehler: 101 > 100
==== Laufzeit-Prüfung bei Zuweisung ====
fn SetSpeed(raw: int64): Speed {
// raw kommt von einem Sensor — Wert zur Compile-Zeit unbekannt
if (raw < 0 || raw > 300) {
PrintLn("Ungültiger Geschwindigkeitswert");
return 0 as Speed; // Fallback
}
return raw as Speed; // Sicher: Wert wurde manuell geprüft
}
==== Range-Typen in DO-178C ====
Range-Typen sind in sicherheitskritischem Code ein bevorzugtes Mittel zur physikalischen Plausibilitätsprüfung:
unit flight_control;
type BankAngle = int32 range -60..60; // Grad (physikalisch begrenzt)
type PitchAngle = int32 range -30..30; // Grad
type Mach = f64; // kein Range auf f64 — nur int-Typen
@flight_crit(DAL-A)
fn ComputeAileron(bank: BankAngle, target: BankAngle): int32 {
// bank und target sind garantiert in [-60, 60]
return (target - bank) as int32;
}
> Range-Typen gibt es nur für Integer-Typen (''int8'' bis ''int64'', ''uint8'' bis ''uint64''). Für Fließkomma-Bereiche muss manuell geprüft werden.
----
===== 3. Der as-Operator =====
Alle Typumwandlungen sind explizit mit ''as''. Keine impliziten Promotionen, kein C-Style-Casting durch Klammern.
==== Zwischen Integer-Typen ====
var big: int64 := 100000;
var small: int32 := big as int32; // Passt — 100000 liegt in int32-Bereich
var byte_v: uint8 := big as uint8; // Truncation: 100000 mod 256 = 160
var u: uint64 := -1 as uint64; // Bit-Reinterpretation: 2⁶⁴−1
// Vorzeichen-Erweiterung (sign-extend) beim Verbreitern
var s8: int8 := -10;
var s64: int64 := s8 as int64; // -10 (korrekt sign-extended)
// Vorsicht: uint → int bei großen Werten
var u32: uint32 := 0xFFFFFFFF;
var i32: int32 := u32 as int32; // -1 (Bit-Reinterpretation)
==== Integer ↔ Float ====
var i: int64 := 42;
var f: f64 := i as f64; // 42.0 (exakt bis ±2⁵³)
var pi: f64 := 3.14159;
var n: int64 := pi as int64; // 3 (truncation Richtung Null, kein Runden)
var neg: f64 := -2.9;
var m: int64 := neg as int64; // -2 (floor toward zero, nicht -3)
==== Integer ↔ Pointer ====
// MMIO-Register adressieren
var addr: int64 := 0x40020000;
var reg: ^uint32 := addr as ^uint32; // Integer → Pointer (MMIO)
var back: int64 := reg as int64; // Pointer → Integer
// Vorsicht: nur in unsafe-Blöcken oder bei MMIO-Code
unsafe {
var gpio: ^uint32 := 0x3FF44004 as ^uint32;
gpio^ := gpio^ | (1 << 5); // Bit 5 setzen
}
==== Enum ↔ Integer ====
enum State { Idle = 0, Running = 1, Error = 2 }
var s: State := State::Running;
var n: int64 := s as int64; // 1
// int64 → Enum: kein Werte-Check — Verantwortung liegt beim Programmierer
var raw: int64 := 99;
var bad: State := raw as State; // Undefined Behavior: 99 ist kein gültiger State
==== Konvertierungs-Regeln im Überblick ====
Alle Regeln für den ''as''-Operator:
^ Von → Nach ^ Verhalten ^ Verlust? ^
| ''int8..64'' → breiter ''intN'' | Sign-extend (Vorzeichen erhalten) | Nein |
| ''int8..64'' → schmäler ''intN'' | Truncation (modular, niedrige Bits) | Möglich |
| ''intN'' → ''uintN'' gleicher Breite | Bit-Reinterpretation | Semantisch |
| ''uintN'' → ''intN'' gleicher Breite | Bit-Reinterpretation | Semantisch |
| ''intN'' → ''f64'' | Exakte Konvertierung (bis ±2⁵³) | Bei großen int64 |
| ''f64'' → ''intN'' | Truncation Richtung Null | Nachkommastellen |
| ''f32'' → ''f64'' | Präzisions-Erweiterung | Nein |
| ''f64'' → ''f32'' | Rounding to nearest, ggf. Inf | Ja |
| Pointer → ''int64'' | Adress-Wert | Nein |
| ''int64'' → Pointer | Integer als Adresse | Unsafe |
| Enum → ''int64'' | Numerischer Wert | Nein |
| ''int64'' → Enum | Kein Werte-Check | Unsafe |
----
===== 4. Der is-Operator =====
''is'' prüft den **Laufzeit-Typ** eines Objekts. Nur für Klassen mit Vererbung (''class extends'') sinnvoll — bei Werttypen und Enums ist der Typ immer statisch bekannt.
type Shape = class { virtual fn Area(): f64; };
type Circle = class extends Shape { pub radius: f64; };
type Rect = class extends Shape { pub w: f64; pub h: f64; };
fn PrintShape(s: Shape): void {
if (s is Circle) {
var c: Circle := s as Circle; // Downcast: sicher nach is-Prüfung
Print("Kreis, Radius: ");
PrintF64(c.radius);
} else if (s is Rect) {
var r: Rect := s as Rect;
Print("Rechteck: ");
PrintF64(r.w);
Print(" × ");
PrintF64(r.h);
}
PrintLn("");
}
fn main(): int64 {
var c: Circle := new Circle();
c.radius := 5.0;
PrintShape(c); // Kreis, Radius: 5.0
var r: Rect := new Rect();
r.w := 3.0; r.h := 4.0;
PrintShape(r); // Rechteck: 3.0 × 4.0
dispose c;
dispose r;
return 0;
}
==== is ohne vorherigen Check — Vorsicht ====
Ein Downcast mit ''as'' ohne ''is''-Prüfung ist möglich, aber undefiniert wenn der Laufzeittyp nicht passt:
fn UnsafeDowncast(s: Shape): void {
var c: Circle := s as Circle; // ⚠ Kein is-Check — Crash wenn s ein Rect ist
PrintF64(c.radius);
}
> In ''@dal(A)''-Code ist ein Downcast ohne vorherigen ''is''-Check ein Compiler-Fehler.
----
===== 5. Typkonvertierung vs. Konvertierungsfunktionen =====
''as'' ist ein reiner Bit- oder Interpretations-Cast — es wird **keine Wertprüfung** durchgeführt. Für inhaltlich korrekte Konvertierungen gibt es Standardfunktionen:
import std.string;
import std.conv;
// Zahlen in Strings (und umgekehrt) — nicht mit as!
var n: int64 := 42;
var s: pchar := IntToStr(n); // "42"
var f: f64 := 3.14;
var fs: pchar := F64ToStr(f, 2); // "3.14"
var parsed: int64 := StrToInt("123"); // 123
var fparsed: f64 := StrToF64("2.5"); // 2.5
// Falsch — as konvertiert nicht Text zu Zahl:
var wrong: int64 := "42" as int64; // Pointer-Wert der Zeichenkette, NICHT 42
^ Aufgabe ^ Mittel ^
| Primitiver Typ → anderen Primitiv-Typ | ''as'' |
| Integer → Float (exakt) | ''as'' |
| Float → Integer (truncation) | ''as'' |
| Zahl → Zeichenkette | ''IntToStr'', ''F64ToStr'' (''std.conv'') |
| Zeichenkette → Zahl | ''StrToInt'', ''StrToF64'' (''std.conv'') |
| Klasse → Basisklasse | ''as'' (Upcast, immer sicher) |
| Basisklasse → Unterklasse | ''is'' prüfen, dann ''as'' (Downcast) |
| Enum → int64 | ''as'' |
| int64 → Enum | Nur wenn Wert zuvor geprüft (via ''match'' oder manuell) |
----
===== 6. Linter-Warnungen bei riskanten Casts =====
''--lint'' aktiviert Warnungen für Casts, die Datenverlust verursachen können:
lyxc main.lyx --lint -o app
warning: narrowing cast int64 → uint8 may truncate
--> main.lyx:42:18
note: value 1000 truncated to 232
warning: float-to-int cast f64 → int32 discards fractional part
--> main.lyx:57:22
In ''@dal(A)''-Modulen werden diese Warnungen automatisch zu **Fehlern** — Datenverlust durch Cast ist nicht zertifizierbar:
@dal(A)
fn ReadSensor(): int32 {
var raw: int64 := GetRawValue();
return raw as int32; // ERROR in @dal(A): narrowing cast ohne explizite Prüfung
// Korrekt:
// if (raw < -2147483648 || raw > 2147483647) { Panic(); }
// return raw as int32;
}
----
===== 7. Zusammenfassung =====
^ Konzept ^ Syntax ^ Anmerkung ^
| Einfacher Alias | ''type X = Y'' | Strukturell identisch mit Y |
| Range-Typ | ''type X = intN range a..b'' | Compile-Zeit-Check für Literale |
| Struct-Typ | ''type X = struct { ... }'' | Wertetyp, Stack-allokiert |
| Klassen-Typ | ''type X = class { ... }'' | Referenztyp, Heap-allokiert |
| Typumwandlung | ''val as TargetType'' | Immer explizit; kein implizites Casting |
| Laufzeit-Typrüfung | ''obj is TypeName'' | Nur für Klassen mit Vererbung |
| Riskanter Cast | ''--lint'' warnt | In ''@dal(A)'' → Fehler |
→ [[lyx_-_programmiersprache:sprache:datentypen|Datentypen — vollständige Typübersicht]]\\
→ [[lyx_-_programmiersprache:sprache:enums|Enums — Aufzählungstypen]]\\
→ [[lyx_-_programmiersprache:sprache:oop|OOP — Klassen, Vererbung, Interfaces]]\\
→ [[lyx_-_programmiersprache:guides:do-178c|DO-178C — Casts in sicherheitskritischem Code]]
Letzte Aktualisierung: 2026-06-05