Fehler als Werte
Zig hat keine Exceptions. Fehlbare Funktionen liefern eine Fehlerunion E!T (Fehler oder Wert); !T lässt den Compiler die Fehlermenge erschließen.
const std = @import("std");
const RechenFehler = error{
DivisionDurchNull,
NegativeWurzel,
};
fn teile(a: i32, b: i32) RechenFehler!i32 {
if (b == 0) return error.DivisionDurchNull;
return @divTrunc(a, b);
}
fn wurzel(x: f64) RechenFehler!f64 {
if (x < 0) return RechenFehler.NegativeWurzel;
return @sqrt(x);
}
fn berechne(a: i32, b: i32) !i32 {
const q = try teile(a, b); // try: Fehler an den Aufrufer weitergeben
return q * 2;
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
try out.print("{d}\n", .{try teile(10, 2)});
const r = teile(1, 0) catch |err| blk: { // catch: Fehler behandeln
try out.print("Fehler: {s}\n", .{@errorName(err)});
break :blk -1;
};
try out.print("{d}\n", .{r});
try out.print("{d}\n", .{teile(9, 0) catch 0}); // Standardwert
if (wurzel(-4)) |w| {
try out.print("{d}\n", .{w});
} else |err| {
try out.print("wurzel scheitert: {s}\n", .{@errorName(err)});
}
if (berechne(5, 0)) |_| {
try out.print("kein Fehler\n", .{});
} else |e| switch (e) { // Fehler in switch prüfen
error.DivisionDurchNull => try out.print("durch null!\n", .{}),
else => unreachable,
}
const zahl = std.fmt.parseInt(u8, "300", 10) catch |e| {
try out.print("parseInt: {s}\n", .{@errorName(e)});
return;
};
try out.print("nie erreicht {d}\n", .{zahl});
}Ausgabe
5 Fehler: DivisionDurchNull -1 0 wurzel scheitert: NegativeWurzel durch null! parseInt: Overflow
Wird ein Fehler bis zu main durchgereicht, beendet Zig das Programm mit einer Fehlermeldung samt Stack-Trace (im Debug-Modus). Der Compiler zwingt dich, jeden Fehler zu behandeln oder weiterzugeben.
defer und errdefer
defer führt Code am Ende des Gültigkeitsbereichs aus, ideal fürs Aufräumen. errdefer nur, wenn der Bereich mit einem Fehler verlassen wird:
const std = @import("std");
fn arbeite(out: anytype, fehler: bool) !void {
try out.print("öffne Ressource\n", .{});
defer out.print("schließe Ressource\n", .{}) catch {};
errdefer out.print("Aufräumen nach Fehler\n", .{}) catch {};
try out.print("arbeite ...\n", .{});
if (fehler) return error.Kaputt;
try out.print("fertig\n", .{});
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
arbeite(out, false) catch {};
try out.print("--\n", .{});
arbeite(out, true) catch |e| try out.print("Fehler: {s}\n", .{@errorName(e)});
// defer-Reihenfolge: rückwärts
{
defer out.print("A ", .{}) catch {};
defer out.print("B ", .{}) catch {};
defer out.print("C\n", .{}) catch {};
}
}Ausgabe
öffne Ressource arbeite ... fertig schließe Ressource -- öffne Ressource arbeite ... Aufräumen nach Fehler schließe Ressource Fehler: Kaputt C B A
Dateien
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
var dir = std.fs.cwd();
{
const datei = try dir.createFile("demo.txt", .{});
defer datei.close();
try datei.writeAll("Zeile 1\nZeile 2\nZeile 3\n");
}
var puffer: [256]u8 = undefined;
const datei = try dir.openFile("demo.txt", .{});
defer datei.close();
const n = try datei.readAll(&puffer);
try out.print("{d} Bytes\n", .{n});
var zeilen = std.mem.splitScalar(u8, puffer[0..n], '\n');
var zaehler: usize = 0;
while (zeilen.next()) |z| {
if (z.len == 0) continue;
zaehler += 1;
try out.print("{d}: {s}\n", .{ zaehler, z });
}
const fehler = dir.openFile("gibt_es_nicht.txt", .{});
try out.print("{any}\n", .{fehler});
try dir.deleteFile("demo.txt");
}Ausgabe
24 Bytes 1: Zeile 1 2: Zeile 2 3: Zeile 3 error.FileNotFound
Sicherheit: Panic, unreachable, assert
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
const arr = [_]i32{ 1, 2, 3 };
var i: usize = 0;
while (i < arr.len) : (i += 1) {
try out.print("{d} ", .{arr[i]});
}
try out.print("\n", .{});
std.debug.assert(arr.len == 3); // bricht im Debug-Modus ab, wenn falsch
// arr[5] wäre ein Panic: index out of bounds
// @panic("Selbst ausgelöst");
// unreachable: behauptet "hier kommt man nie hin"
try out.print("alles in Ordnung\n", .{});
}Ausgabe
1 2 3 alles in Ordnung
Merke
- Fehler sind Werte:
error{...},E!T,try(weiterreichen),catch(behandeln) if (res) |wert| ... else |err| ...undswitchauf Fehlerdeferräumt am Blockende auf,errdefernur bei Fehler- Dateien:
std.fs.cwd().createFile/openFile, immerdefer datei.close() - Debug-Modus fängt Überläufe, Grenzfehler und
unreachableab
Aufgabe
Schreibe eine Funktion parseAlter(text) !u8, die bei ungültiger Eingabe einen eigenen Fehler liefert.