🎓 A1Learner
🔍
0 of 46 lessons completed

Zig C Error Handling

C error handling is the pattern of checking a C call's return value and reading its error number when the call fails. The error number names the failure, and a message function turns it into words.

A failed open

Opening a missing file fails and returns null. The program reads the error number and prints it.

const std = @import("std");
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("errno.h");
});
pub fn main() !void {
    const f = c.fopen("no-such-file-xyz.txt", "r");
    const out = std.io.getStdOut().writer();
    if (f == null) {
        try out.print("open failed errno={d}\n", .{c.__errno_location().*});
    } else {
        try out.print("opened\n", .{});
    }
}

Zero is not always failure

Converting non-numeric text returns zero without failing. The error number stays zero, showing no failure happened.

const std = @import("std");
const c = @cImport({
    @cInclude("stdlib.h");
    @cInclude("errno.h");
});
pub fn main() !void {
    const v = c.strtol("abc", null, 10);
    const out = std.io.getStdOut().writer();
    try out.print("value={d} errno={d}\n", .{ v, c.__errno_location().* });
}

A failed remove

Removing a missing file also fails. The program checks the return value and prints the error number.

const std = @import("std");
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("errno.h");
});
pub fn main() !void {
    const r = c.remove("no-such-file-xyz.txt");
    const out = std.io.getStdOut().writer();
    if (r != 0) {
        try out.print("remove failed errno={d}\n", .{c.__errno_location().*});
    } else {
        try out.print("removed\n", .{});
    }
}

Example

const std = @import("std");
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("string.h");
    @cInclude("errno.h");
});
pub fn main() !void {
    const f = c.fopen("no-such-file-xyz.txt", "r");
    const out = std.io.getStdOut().writer();
    if (f == null) {
        const e = c.__errno_location().*;
        try out.print("open failed errno={d} message={s}\n", .{ e, c.strerror(e) });
    } else {
        try out.print("opened\n", .{});
    }
}

Key points

  • Errno: errno is a number set by a C library call when that call fails.
  • Return value: a return value is the result a function hands back, and a special value signals failure.
  • A failing open returns a null pointer, so the program must test for null before reading the error number.
  • The error number is only meaningful right after a failed call, so the program reads it before any other C call.
  • A message function turns an error number into a readable sentence for the user.
  • A return of zero is not always a failure, so the program must know what failure looks like for each function.

The example calls open on a missing file and tests the result for null. It reads the error number at once, before any other C call. It turns the number into words and prints the number and the message together.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

When should a program read the error number after a C call fails?