Skip to content

Examples

Examples are small programs kept inside the project.

Examples are small programs kept inside the project.

They show how the code is meant to be used. They also check that public APIs still compile.

A common layout is:

project/
├── build.zig
├── src/
│   └── root.zig
└── examples/
    ├── hello.zig
    └── copy.zig

An example is an ordinary Zig program:

const std = @import("std");
const mylib = @import("mylib");

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

The build file creates an executable for it:

const example = b.addExecutable(.{
    .name = "hello-example",
    .root_module = b.createModule(.{
        .root_source_file = b.path("examples/hello.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

If the example imports the project library, attach the module:

const lib_mod = b.createModule(.{
    .root_source_file = b.path("src/root.zig"),
    .target = target,
    .optimize = optimize,
});

example.root_module.addImport("mylib", lib_mod);

Now examples/hello.zig can use:

const mylib = @import("mylib");

Add a named step:

const examples_step = b.step("examples", "Build examples");
examples_step.dependOn(&example.step);

Run it:

zig build examples

For several examples, use a helper function or a loop:

const example_names = [_][]const u8{
    "hello",
    "copy",
};

const examples_step = b.step("examples", "Build examples");

for (example_names) |name| {
    const path = b.fmt("examples/{s}.zig", .{name});

    const exe = b.addExecutable(.{
        .name = b.fmt("{s}-example", .{name}),
        .root_module = b.createModule(.{
            .root_source_file = b.path(path),
            .target = target,
            .optimize = optimize,
        }),
    });

    exe.root_module.addImport("mylib", lib_mod);
    examples_step.dependOn(&exe.step);
}

Examples should stay short. Each one should show one use case.

Good examples have names like:

hello
copy-file
parse-json
serve-http

Bad examples have names like:

test1
demo2
misc
stuff

An example can also have a run step:

const run_example = b.addRunArtifact(example);

const run_example_step = b.step("run-example", "Run hello example");
run_example_step.dependOn(&run_example.step);

Then:

zig build run-example

builds and runs it.

For many examples, it is usually enough to build them. Running them may require files, ports, network access, or command-line arguments. Keep automatic example steps deterministic.

Examples are not a substitute for tests. Tests check exact behavior. Examples show intended use.

A good project usually has both.

Exercise 15-21. Create an examples/hello.zig file.

Exercise 15-22. Add an examples build step.

Exercise 15-23. Make the example import a module from src/root.zig.

Exercise 15-24. Add a second example and build both with one command.