Mar 20, 2022

My views on Zig (2022)

Zig is a general-purpose programming language. It is simple and does not have any hidden control flow or allocations.

The language is clear, straight-forward, and readable.

Zig directly competes with C and will be a better alternative for C in the future.

Things that I love about ziglang:

Simple language

Building a simple language is easier said than done. Programmers need rich features in a programming language, but when the programming language supports more features, it becomes sluggish and bloated.

For example, A simple hello-world program in zig will look like this.

// hello.zig
const std = @import("std");

pub fn main() !void {
 const stdout = std.io.getStdOut().writer();
 try stdout.print("Hello!, {s}\n", .{"world"});
}

In the first line, we are importing the std library (curious why we have @ before import check here). We import the std library and assign it to std using the const keyword.

Next, we define the main function. As in other languages, the main specifies the zig compiler where to start the program execution. The function definition commences with the pub fn keyword. As you might have guessed, pub fn means public function.

Note: The pub specifier exports the function.

Then we have the function name main. After the function name and args (we do not have any in this case), we specify the return type. The return type here is !void. The ! specifies that the function might return error. We can expand the !void something like <error-type> or <void>.

Next, we take the writer function from the imported std library.

Note: It is too long!!! But it clearly says what the programmer is trying to do and what the compiler will do.

Finally, we use print from the method from the stdout to print the actual strings out. The print statement is similar to how we will do in other low-level languages like C / Rust.

Note: .{}. in the second argument to the print function. It is an anonymous struct literal. Zig compiler infers the type of anonymous struct literal.

That is it. We can run this zig code using zig run hello.zig.

As you can see, the function is simple and easier to understand. The language neither has macros nor magically print the result.

No magic here!

No Null pointers

Oh yeah! Everyone agrees Null Pointer is a billion-dollar mistake.

Zig does not have null, but they provide optional. Optional type provides safety and better readability.

const optional_world_answer: ?i32 = 42;

The ? specifies the type is optional. It can either accept null or any i32 integer. null is an allowed type in ziglang.

We can safely access the optional using the if-let syntax.

if (optional_world_answer) |world_answer| {
 // here `world_answer` is i32 and not null.
}

Optional makes your language verbose, the benefits of them in safety, readability stands out.

Error handling

Zig is inclusive of errors too. It handles errors as values. The zig compiler ensures errors are handled and complains when they are not.

For example,

fn get_i64() !i64 {
 return error.notImplemented;
}

pub fn main() void {
 // we are basically discarding the result
 _ = get_i64();
}

The above code will throw a compiler error that we are not handling error and the error gets discarded. Always remember errors are values. We can fix it by using catch or try.

// error handling with `catch`
let number = get_i64() catch |err| {
 // do something with error
};

// error handling with `try`
let number = try get_i64();

// well when you are sure that it won't error
let number = try get_i64() catch unreachable

Zig provides refreshing error handling.

Comptime

Zig provides a comptime keyword to specify that a particular value and type are available during the compile-time. You can use comptime in three different places.

  1. parameters
  2. variables
  3. expressions

comptime in parameters

Zig uses the comptime in parameters to duck type and implements generics.

fn generic_fn(comptime T: type, a: T, b: T) {...}

We can call generic_fn like the following:

generic_fn(i32, 10, 11);

The function call-site expects type as a comptime parameter.

comptime in variables

Comptime in variables is another good feature in zig. It guarantees that the compiler will perform all the load and store operations during the compile time itself.

comptime in expressions

Similarly, we can mark an expression as comptime such that it will evaluate at the compile time.

Other features that stand out are:

  • Easier integration with C and other C-like languages.
  • Std library code is readable.
  • Simple grammar for the language.

The things that need to improve in zig are:

  • Although error handling is refreshing, there is no way to pass a context with errors.
  • Extreme importance on readability leads you to write low-level code.
  • The language is in its very early days, and it will be interesting to see how it evolves (with more features).

Overall, Zig has an excellent future. It will be a replacement for C, and I hope it will succeed.

To explore further

Up Next