Seth Barrett

Daily Blog Post: September 7th, 2023

Zig

September 7th, 2023

Part 6: Data Structures in Zig

Welcome to the sixth installment of our "Getting Started with Zig on MacOS" series. In this part, we'll explore the essential data structures in Zig, including arrays, slices, structures (structs), enums, and tagged unions. Understanding these data structures is crucial for organizing and manipulating data effectively in your Zig programs.

Arrays and Slices

Arrays

An array in Zig is a fixed-size collection of elements of the same data type. You declare an array by specifying its type, size, and optionally initializing its elements:

const myArray: [5]i32 = [1, 2, 3, 4, 5];

In this example, we declare an integer array of size 5 and initialize it with values.

Slices

Slices are a more flexible way to work with arrays in Zig. A slice represents a portion of an array, and it can change in size dynamically. You can create a slice from an array or another slice:

const myArray: [5]i32 = [1, 2, 3, 4, 5];
const mySlice = myArray[1..4]; // Creates a slice containing [2, 3, 4]

Slices are particularly useful for working with arrays of unknown or variable sizes.

Structures (Structs)

Structures, often referred to as structs, allow you to create custom data types by combining multiple variables of different types into a single entity. Here's an example of defining and using a struct in Zig:

const std = @import("std");

const Person = struct {
    name: []const u8,
    age: i32,
};

fn main() void {
    const alice = Person{ .name = "Alice", .age = 30 };
    const bob = Person{ .name = "Bob", .age = 25 };

    std.debug.print("Name: {}, Age: {}\n", .{alice.name, alice.age});
    std.debug.print("Name: {}, Age: {}\n", .{bob.name, bob.age});
}

In this example, we define a Person struct with name and age fields and create two instances of it.

Enums and Tagged Unions

Enums

Enums, short for enumerations, are a way to define a type with a fixed set of named values. They are often used to represent a limited set of options or states. Here's an example:

const Status = enum {
    OK,
    Error,
};

const status = Status.OK;

In this example, we define an enum Status with two values: OK and Error.

Tagged Unions

Tagged unions are a powerful feature in Zig that allows you to define a type that can hold one of several different values. Each value is associated with a tag that identifies its type. Tagged unions are particularly useful for handling complex data structures and error handling:

const std = @import("std");

const Result = union {
    Ok: i32,
    Err: []const u8,
};

fn divide(a: i32, b: i32) Result {
    if (b == 0) {
        return Result.Err("Division by zero");
    }
    return Result.Ok(a / b);
}

fn main() void {
    const result1 = divide(10, 2);
    const result2 = divide(8, 0);

    std.debug.print("Result 1: {}\n", .{result1.Ok});
    std.debug.print("Result 2: {}\n", .{result2.Err});
}

In this example, we define a tagged union Result that can hold either an Ok value of type i32 or an Err value of type []const u8.

What's Next?

In this part, you've explored the fundamental data structures in Zig, including arrays, slices, structs, enums, and tagged unions. These data structures provide you with the tools to organize and manipulate data effectively in your Zig programs.

In Part 7, we'll dive into pointers and memory management in Zig. You'll learn how to work with pointers, allocate and deallocate memory, and gain a deeper understanding of Zig's safety features. Stay tuned for more Zig insights and practical examples. Happy coding!