Hassle-free error management
Consider you write a function
fn div(a: u8, b: u8) -> u8 {
a / b
}
If later you find an error case that needs to be handled updating the code
usually will not require much extra addition. error DivideByZero;
fn div(a: u8, b: u8) -> %u8 {
if (b == 0) {
error.divideByZero
} else {
a / b
}
}
The caller can choose to ignore errors using `%%div(5, 1)` or can propagate
errors to the caller, similar to Rust's `try!`, `?` using `%return div(5, 2)`.I find this so easy that I'm much more inclined to think about edge cases and handle errors up front. I find when writing Rust the extra setup and management of errors adds a fair bit of tedium (although to be fair, with error-chain and proper setup at the beginning of a project this isn't too bad).
Compile-time programming
Zig has pretty strong compile-time programming support. For example, its printf formatting capability is all written in userland code [1]. It doesn't at this moment support code-generation like D's mixins but I personally have not found this too problematic.
Generic functions can be written in a duck-typing fashion. With compile-time assertions the inputs can be limited to what they need pretty clearly and the errors during usage are pretty self-explanatory.
error Overflow;
pub fn absInt(x: var) -> %@typeOf(x) {
const T = @typeOf(x);
comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
comptime assert(T.is_signed); // must pass a signed integer to absInt
if (x == @minValue(@typeOf(x))) {
return error.Overflow;
} else {
@setDebugSafety(this, false);
return if (x < 0) -x else x;
}
}
Zig doesn't have any form of macros. Everything is done in the language itself.