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 keindeferund 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
