Urgent.News

What's breaking now, across thousands of outlets.

Tech

Why Zig over C for Systems Programming

When comparing Zig to C for systems programming, it is essential to recognize that both languages share similarities, such as being minimalistic, lacking a garbage collector, and closely interacting with the hardware. However, there are specific aspects of C that can be time-consuming, which Zig addresses more efficiently.

One such challenge in C is resource management. When a function acquires several resources and must release them in reverse order if something fails, C developers often resort to using the "goto" statement. Although this approach works, it becomes tedious due to the necessity of aligning labels with allocation orders manually. In contrast, Zig simplifies this process by having each "errdefer" sit directly below the allocation it is undoing and running only when an error occurs.

This method ensures that cleanup occurs in the correct order without the need for manual label alignment.

Another concern in C is proper testing of failure paths. In C, testing these paths typically requires a wrapper around malloc() or external tools, which can be cumbersome. Zig, however, offers a built-in solution using the std.testing.FailingAllocator type. This type fails on the first, second, or third allocation depending on the value of fail_index, allowing for easier testing of all possible failure cases.

C also presents issues with array handling when passed to functions. In C, arrays are passed as pointers, and length information is often passed separately, if at all. This can lead to off-by-one errors, as seen in a C function that incorrectly sums an array's elements due to an off-by-one mistake. Zig overcomes this issue by using slices, which carry their length, ensuring that the correct range is always used for array operations.

String manipulation in C also poses potential problems. C strings are essentially slices of bytes with a length, requiring functions like strlen() to traverse memory looking for a null byte and often resulting in buffers that are one byte short due to forgotten terminators. Zig eliminates this concern by using strings as slices of bytes with an inherent length, removing the need for separate strlen() calls and eliminating the possibility of buffer overflow due to missing terminators.

Error handling in C is often accomplished through various means such as returning -1 or NULL, setting errno, or returning an error code. However, this flexibility can lead to inconsistent error handling throughout the codebase. Zig, on the other hand, incorporates errors as part of the return type, making it clear when an error might occur. The compiler refuses to compile code that ignores an error, ensuring that developers must explicitly handle potential errors, similar to the approach found in Go.

The C preprocessor relies on text manipulation and lacks any understanding of the C language itself. This results in numerous #define directives, #ifdef lines, and conditional compilation for various operating systems, which can become unwieldy in large projects. Zig's approach to compile-time code is different; it leverages ordinary Zig code for such tasks, such as using #ifdef conditions, simplifying the overall process.

The C standard permits undefined behavior regarding signed integer overflow, allowing compilers to remove code they deem unreachable. This can lead to unexpected results, as exemplified by a C program that adds 1 to INT_MAX and outputs -2147483648. Zig, however, rigorously checks integer overflow in both debug and safe modes, stopping the program with a panic: integer overflow message and the corresponding line number.

When wrapping is desired, Zig provides the +% operator, while the +| operator ensures the result stops at the maximum value.

For cross-compiling, C compilers like gcc or clang can become cumbersome, especially when building for different architectures or operating systems. The zig cc command, however, functions as a C compiler compatible with C code, allowing for easy cross-compilation without the need for additional installations. Moreover, it incorporates the undefined behavior sanitizer in the debug mode, ensuring that the program will halt with a panic: signed integer overflow message in case of any integer overflow issues.

To integrate C code into a Zig project, Zig 0.17 eliminated the @cImport() functionality, replacing it with the zig translate-c command. This change allows for seamless migration of C projects to Zig, file by file, without requiring a complete rebuild of the project.

Lastly, while both C and Zig share a level of stability, C's long-standing presence has led to a vast array of tools, books, and knowledge available to its developers. Zig, being a newer language, still requires some adaptation for developers accustomed to C, but its growing community and expanding ecosystem ensure that it is becoming more accessible over time.

Written by urgent.news from Hacker News's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at mtsoukalos.eu →

More in Tech

More from Friday 13 November →