====== Lyx – Generics & Traits ====== Generics ermöglichen es, Funktionen und Strukturen zu schreiben, die mit verschiedenen Datentypen arbeiten, ohne den Code mehrfach kopieren zu müssen. **Traits** definieren dabei die Anforderungen (Interfaces), die diese Typen erfüllen müssen. ===== 1. Generische Funktionen ===== Eine generische Funktion verwendet Typparameter in spitzen Klammern ''''. fn swap(var a: T, var b: T) { let temp := a; a := b; b := temp; } fn main() { var x := 10; var y := 20; swap(x, y); // T wird zu int } * **Monomorphisierung**: Der Compiler erzeugt für jede genutzte Typ-Kombination eine eigene, optimierte Version der Funktion im Maschinencode. ===== 2. Traits (Schnittstellen) ===== Traits definieren ein Set von Methoden, die ein Typ implementieren muss. Sie dienen als "Constraints" (Einschränkungen) für Generics. trait Drawable { fn draw(); } type Circle = struct { radius: f32 } // Implementierung des Traits für Circle impl Drawable for Circle { fn draw() { // Logik zum Zeichnen } } ===== 3. Generic Constraints ===== Mithilfe von Traits kann man einschränken, welche Typen für ein Generic zulässig sind. Dies ist essenziell, um sicherzustellen, dass benötigte Operationen (wie Addition oder Vergleiche) auf dem Typ ''T'' existieren. // T muss das Trait 'Comparable' erfüllen fn get_max(a: T, b: T): T { if (a > b) { return a; } return b; } ===== 4. Generische Strukturen & Klassen ===== Auch komplexe Datentypen können generisch definiert werden. type Box = struct { content: T, is_empty: bool } var int_box: Box := Box { content: 42, is_empty: false }; ===== 5. Statische vs. Dynamische Dispach ===== Lyx bevorzugt aus Performance-Gründen die statische Auflösung. ^ Merkmal ^ Statische Generics (Standard) ^ Dynamische Traits (v0.9.5+) ^ | **Mechanismus** | Code-Duplizierung (Monomorph) | Virtual Table (V-Table) | | **Performance** | Maximal (Inlining möglich) | Overhead durch indirekten Sprung | | **Binärgröße** | Steigt pro Typ-Instanz | Bleibt konstant | | **Zertifizierung**| Exzellent (WCET berechenbar) | Schwierig (indirekte Sprünge) | ===== 6. Best Practices im Safety-Umfeld ===== * **Vermeide tiefe Generic-Hierarchien**: In DAL-A Systemen sollte die Code-Komplexität überschaubar bleiben, um die Testbarkeit zu gewährleisten. * **Traits für Hardware-Abstraktion**: Nutze Traits, um Treiber-Schnittstellen zu definieren (z.B. ''trait UART''). So kann der Applikationscode generisch gegen den Trait geschrieben und für verschiedene Hardware-Backends (ESP32, RISC-V) spezialisiert werden. * **Constraints nutzen**: Verwende immer explizite Constraints, damit Fehlermeldungen bereits beim Aufruf der generischen Funktion entstehen und nicht erst tief im Compiler-Backend.