Inhaltsverzeichnis

Fehler-Konventionen der Standardbibliothek

Die Lyx-Standardbibliothek hat kein try/catch, keine Exceptions, keine impliziten Fehlerzustände. Jede Funktion, die fehlschlagen kann, signalisiert das über ihren Rückgabewert — immer sichtbar, immer explizit. Diese Seite erklärt die vier Konventionen, die sich quer durch alle std-Units ziehen.

Fehlerbehandlung (Sprachebene) · Handle-Konzept · Rohspeicher


Konvention 1: Kernel-Syscall → Rückgabe < 0

Alle Wrapper um Linux-Syscalls (open, read, write, socket, epoll_wait …) geben bei Fehler einen negativen Wert zurück. Dieser Wert ist der negierte errno-Code.

import std.fs;

var fd: int64 := open("/etc/hosts", 0, 0);
if (fd < 0) {
    // fd enthält negierten errno: z.B. -2 = ENOENT, -13 = EACCES
    PrintLn("open fehlgeschlagen: errno = " + IntToStr(0 - fd));
    return -1;
}

var n: int64 := read(fd, buf, 4096);
if (n < 0) {
    // Lesefehler
    close(fd);
    return -1;
}
if (n == 0) {
    // EOF — kein Fehler, aber keine Daten mehr
}

Rückgabewert Bedeutung
≥ 0 Erfolg (oft: Anzahl gelesener Bytes, neuer fd, 0 = OK)
< 0 Fehler; Wert = -errno (z.B. -2 = ENOENT)

Alle Low-Level-Units nutzen diese Konvention: std.fs_ext, std.pipe, std.signals, std.sched, std.mmap_ext, std.net.epoll.


Konvention 2: Userspace-Handle → Rückgabe 0 bei Fehler

Funktionen, die einen Userspace-Handle (Pointer auf alloc'd Struct) erzeugen, geben 0 zurück wenn die Erstellung fehlschlägt.

import std.db.sqlite;
import std.db.postgres;

// SQLite: 0 = Fehler
var db: int64 := SQLiteOpen("/data/app.db");
if (db == 0) {
    PrintLn("SQLite nicht geöffnet");
    return -1;
}

// Prepared Statement: 0 = Fehler (SQL-Syntaxfehler, Tabelle nicht vorhanden …)
var stmt: int64 := SQLiteStmtPrepare(db, "SELECT * FROM users");
if (stmt == 0) {
    Print("Prepare fehlgeschlagen: " + SQLiteErrmsg(db));
    SQLiteClose(db);
    return -1;
}

Units die dieser Konvention folgen: std.db.sqlite, std.pdf.builder (intern), std.net.socket.


Konvention 3: Zweistufige Prüfung (PostgreSQL-Muster)

Manche Verbindungen scheitern zweistufig: Die TCP-Verbindung kann erfolgreich sein, aber Auth oder Protokoll-Handshake danach fehlschlagen. Solche Units haben eine IsConnected-Funktion.

import std.db.postgres;

var conn: int64 := PGConnect("127.0.0.1", 5432, "mydb", "user", "wrongpass");
// PGConnect gibt 0 zurück NUR bei TCP-Fehler (Host nicht erreichbar).
// Bei falschem Passwort liefert es einen gültigen conn != 0.

if (conn == 0) {
    PrintLn("TCP-Verbindung fehlgeschlagen");
    return -1;
}

// Zweite Prüfung: Auth- oder Protokollfehler
if (PGIsConnected(conn) == 0) {
    PrintLn("Verbindung aufgebaut, aber Auth-Fehler: " + PGError(conn));
    PGClose(conn);
    return -1;
}

// Jetzt ist conn sicher verwendbar

Diese Konvention gilt für alle Protokolle mit einem mehrstufigen Handshake (TCP-Verbindung + Authentifizierung). Immer beide Prüfungen durchführen.

Konvention 4: Fehlercode-Konstanten

Einige Units definieren eigene Rückgabe-Code-Konstanten anstelle von negativen Zahlen oder 0/1. Das trifft vor allem auf Units zu, die externe Protokolle wrappen.

SQLite

import std.db.sqlite;

var rc: int64 := SQLiteStmtStep(stmt);

if (rc == SQLITE_ROW) {
    // Zeile verfügbar — Column-Accessoren verwenden
    var name: pchar := SQLiteColumnText(stmt, 0);
    PrintLn(name);
}
else if (rc == SQLITE_DONE) {
    // Alle Zeilen verarbeitet — kein Fehler
}
else {
    // Fehler: rc enthält SQLite-Fehlercode
    PrintLn("Step-Fehler: " + IntToStr(rc));
    // SQLITE_BUSY=5, SQLITE_ERROR=1, SQLITE_CONSTRAINT=19 …
}

Konstante Wert Wann
SQLITE_OK 0 Erfolg (nicht Step/Exec)
SQLITE_ROW 100 Nächste Zeile verfügbar
SQLITE_DONE 101 Alle Zeilen verarbeitet
alle anderen 1–99 Fehler

Fehler weitergeben

Da Lyx keine Exceptions hat, wird ein Fehler durch Rückgabe eines Fehlerwerts nach oben propagiert. Das übliche Muster:

import std.db.sqlite;
import std.alloc;

// Gibt 0 bei Erfolg, -1 bei Fehler zurück
fn LoadConfig(path: pchar): int64 {
    var db: int64 := SQLiteOpen(path);
    if (db == 0) { return -1; }

    var stmt: int64 := SQLiteStmtPrepare(db, "SELECT key, value FROM config");
    if (stmt == 0) {
        SQLiteClose(db);
        return -1;
    }

    while (SQLiteStmtStep(stmt) == SQLITE_ROW) {
        var key: pchar := SQLiteColumnText(stmt, 0);
        var val: pchar := SQLiteColumnText(stmt, 1);
        ApplyConfig(key, val);
    }

    SQLiteStmtFinalize(stmt);
    SQLiteClose(db);
    return 0;   // Erfolg
}

fn main(): int64 {
    if (LoadConfig("/data/app.db") != 0) {
        PrintLn("Konfiguration konnte nicht geladen werden");
        return 1;
    }
    return 0;
}


Ressourcen bei Fehler freigeben

Der häufigste Fehler in Lyx-Code: Bei einem Fehler in der Mitte einer Initialisierungssequenz werden bereits erstellte Ressourcen nicht freigegeben.

// Schlecht — Memory/Resource Leak bei Fehler:
fn BadInit(): int64 {
    var fd:   int64 := open("/data/config", 0, 0);
    var db:   int64 := SQLiteOpen("/data/app.db");
    var conn: int64 := PGConnect("localhost", 5432, "db", "u", "p");

    if (PGIsConnected(conn) == 0) {
        return -1;   // fd und db wurden nie geschlossen!
    }
    return 0;
}

// Richtig — Cleanup bei jedem Fehlerfall:
fn GoodInit(): int64 {
    var fd: int64 := open("/data/config", 0, 0);
    if (fd < 0) { return -1; }

    var db: int64 := SQLiteOpen("/data/app.db");
    if (db == 0) {
        close(fd);
        return -1;
    }

    var conn: int64 := PGConnect("localhost", 5432, "db", "u", "p");
    if (conn == 0 || PGIsConnected(conn) == 0) {
        SQLiteClose(db);
        close(fd);
        return -1;
    }

    // Alles OK — Ressourcen werden von der aufrufenden Schicht verwaltet
    return 0;
}

Faustregel: Jede Ressource, die nach einem gescheiterten Schritt noch offen ist, muss in dem Fehler-Branch explizit geschlossen werden. Lyx hat kein defer und keine Destruktoren.

Schnell-Referenz

Situation Fehlerindikator Beispiel
Syscall / Low-Level I/O < 0 open, read, EpollCreate
Handle-Erstellung (Userspace) == 0 SQLiteOpen, PdfCreate
Mehrstufiger Handshake == 0 + IsConnected == 0 PGConnect + PGIsConnected
SQLite Step SQLITE_ROW / SQLITE_DONE / Fehlercode SQLiteStmtStep
Binding / Column-Accessor bool (true = Erfolg) SQLiteBindInt, SQLiteColumnIsNull
Ressource freigeben kein Rückgabewert SQLiteClose, PGClose, PdfFree

Fehlerbehandlung auf Sprachebene (Result, Tuple, panic)
Handle-Konzept — Kernel-fd vs. Userspace-Handle
std.error — errno-Konstanten und Fehlermeldungen

Letzte Aktualisierung: 2026-06-05