One Month of Zig: Projects, Pitfalls, and Progress

One Month of Zig: Projects, Pitfalls, and Progress
An enemy crab approaches!

Goals:
– Understand Zig as a language and ecosystem as quickly as possible
Work on a personal project that helps me learn and (hopefully) make something worth sharing
– Make progress regularly

Learning Zig

The most straightforward place to start is the Learn tab on Ziglang.org. I followed the getting started guide, and poked around some of the links like the Code Examples to get a feel for the syntax and Tools to get my IDE ready to go. Surprisingly smooth onboarding experience for a systems language. Within a short time I had gone from simple "Hello, world!" to following my first Zig tutorial, zig-by-example.

Zig-by-example was a great introduction to Zig's core concepts, like variables, numeric types, loops, and pointers. But something was missing—something I hadn’t even realized I took for granted in other languages: ergonomic string handling. "Hello, world!" is easy to print out, but where's the string type?

Strings are different in Zig. A string is of type []const u8. That's right, strings are arrays of characters in Zig. That sounds basic, but I found working with strings was harder than expected. This blog post from Huy titled Zig Strings in 5 minutes helped me grok the basics. It was a first step towards understanding what sort of wizardry was possible with strings in Zig. I learned how to split and slice comma-separated-values, satisfying my initial curiosity.

Here’s a little experiment I wrote to wrap my head around slices, iteration, and string character access:
const std = @import("std");

pub fn main() !void {
    // strings are arrays in Zig
    // https://www.huy.rocks/everyday/01-04-2022-zig-strings-in-5-minutes
    const classesString = "Barbarian,Wizard,Rogue";
    var classes = std.mem.splitAny(u8, classesString, ",");

    var counter: usize = 0;

    while (classes.next()) |class| {
        // print each player class name
        std.debug.print("{s} ", .{class});

        // number of characters in the class's string
        // std.debug.print("{d}, ", .{class.len});

        // each character in the string is an element of the array
        for (class, 0..) |character, index| {
            _ = index;
            std.debug.print("{c}-", .{character});
        }

        // does a counter work here? - YES
        std.debug.print("{any}\n", .{counter});
        counter += 1;
    }
}

One last string-related tip: the built-in function @tagName is incredibly handy for working with enums. I almost always include a helper like this when working with structs in my game.

// Fantasy RPG class types
pub const kind = enum {
    Barbarian,
    Wizard,
    Rogue,
    // etc.
}

pub const Character: type = struct {
    hp: u32,
    mana: u32,
    kind: kind,
    
    pub fn kindName(self: *Character) []const u8 {
        return @tagName(self.kind);
    }
}

Helpful Zig Resources

As I fumbled through the basics, a few resources stood out and made the journey smoother:

Zig Learn Tab — the official starting point. Solid for orientation and setup.

zig-by-example — great for hands-on learners. Clear, concise examples that build confidence fast.

Zig Strings in 5 Minutes by Huy — genuinely helped me grok slices and string handling early on.

zig.guide — a well-organized, human-friendly tour through Zig, with great breakdowns of common pain points. I regularly reference this for understanding and insight.

Official Zig Documentation
You’ll want to leave a tab open, or find your local copy, especially when using with the standard library.

Each of these is worth your time, especially when just getting started. The community and documentation are very helpful. I recommend checking out https://ziggit.dev to get a taste of the community.


What’s Next

In the next post, I’ll dive into my hobby project and show what I’ve actually been building—plus what it feels like to write game systems from scratch in Zig. The good, bad, and ugly, of course. It's ugly Zig code, but if it's good enough for the compiler, it's good enough for me.