My views on Ziglang
Tags: zig, languages, beginners, views
Published: 2022-03-20 19:21:36

[Originally posted on](https://sendilkumarn.com/blog/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.

```zig
// 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](https://ziglang.org/documentation/0.8.1/#Builtin-Functions)). 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.

```zig
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.

```zig
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,

```zig
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`.

```zig
// 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
1. variables
1. expressions

### comptime in parameters

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

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

We can call `generic_fn` like the following:

```zig
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

- [Why Zig?](https://ziglang.org/learn/why_zig_rust_d_cpp/)
- [Zig documentation](https://ziglang.org/documentation/master/)
- [Ziglings](https://github.com/ratfactor/ziglings)
- [My AOC experiments in Zig](https://github.com/sendilkumarn/aoc2021InZig)