Random Musings

O for a muse of fire, that would ascend the brightest heaven of invention!

Starting with zig

Friday, 5 Feb 2021 Tags: zig

zig is a very nice (and early) simple language, sitting somewhere orthogonally between C, rust, and go:

Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.

It’s moving fast, very fast, so while the current release is 0.7.1 you should be running the master branch of the compiler and updating regularly. Obviously this close to the sharp end, you can expect some breakages, but there’s a very welcoming community out there already, including irc channel, discord & matrix as well, and frequent twitch streams, most easily found on the discord channel. Keep an eye out for:

To get a feel for zig, you can take a look at the samples and catch a few of the archived videos or stay current with zig showtime, a regular and fascinating series of chats and interviews with various zig-powered projects and developers. I guarantee you’ll learn something.

The compiler toolchain today is still based off llvm, but the #1 focus is to get it to be self-hosting. You can read up on zig’s features on the website.

why zig?

I tend to develop software in a mix of high-level functional programming languages, such as Elixir, and also to integrate awesome low-level functionality written in C, or provided by the base FreeBSD OS as well, which typically has a C syscall interface.

I’m particularly interested in combining FreeBSD’s native jail virtualisation, security models, and fast networking (which are best accessed via C or a similar language level interface), and distributed systems, which I tinker with in Erlang/OTP and Elixir.

Most of the time, a robust type system (rust) isn’t what I need, and splicing go code and C together isn’t particularly fun either - I was looking for something that would sit between these languages, low friction between FreeBSD’s C interfaces, still handy for JSON related stuff, and able to be tightly integrated into the Erlang BEAM environment.

The zigler project is what really brought me to zig - an amazing library that allows inlining high-performance zig code, inside high-level concurrent Elixir code. As this code needs to run inside a long-running concurrent Virtual Machine, we need to be certain it can’t crash. And zigler handles this beautifully, by leveraging zig’s expectations that you bring your own memory allocator and just letting the BEAM handle that.

The usual C compiler optimisation levels are available (Debug, Release, Fast, Small) and as an added bonus, there’s no C preprocessor macros, but the full power of zig is available at compile-time aka comptime.

Zig, for me, sits in a sweet spot with native access to C libraries, fast compilation, and some type & memory safety, although not as strong as OCaml or Rust. Zig makes cross-compilation very easy, and fast, and has a tight focus on “no magic” — no hidden control flow, or memory allocations. What you see is what you get!

Zig is right in that sweet spot, with almost go-speed compile times, great documentation, and a helpful community.

show me some code

This is about as minimal as you can get - hello world, using a standard library for output, and a string literal being passed back into the library:

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

pub fn main() void {
    std.debug.print("hello, {s}!\n", .{"World"});

The optimised builds produce reasonably small binaries, at 50Kb, but even the fat version, at 0.5Mb is still 1/3 the size of a similar go program, which clocked in at 1.9Mb.

$ zig run baby-steps/hello.zig
hello, World!

$ zig build-exe --help
  -O [mode]                 Choose what to optimize for
    Debug                   (default) Optimizations off, safety on
    ReleaseFast             Optimizations on, safety off
    ReleaseSafe             Optimizations on, safety on
    ReleaseSmall            Optimize for small binary, safety off

$ zig build-exe -O Debug        --name hello-debug  ./baby-steps/hello.zig
$ zig build-exe -O ReleaseSafe  --name hello-safe   ./baby-steps/hello.zig
$ zig build-exe -O ReleaseFast  --name hello-fast   ./baby-steps/hello.zig
$ zig build-exe -O ReleaseSmall --name hello-small  ./baby-steps/hello.zig

$ l hello*
-rwxr-xr-x  1 dch  staff   557K Feb  5 16:00 hello-debug*
-rwxr-xr-x  1 dch  staff    49K Feb  5 16:00 hello-fast*
-rwxr-xr-x  1 dch  staff   516K Feb  5 16:01 hello-safe*
-rwxr-xr-x  1 dch  staff    49K Feb  5 16:00 hello-small*

I guess when the program gets more complicated, it will be more interesting for optimisation??