Skip to content

habedi/zig-dbc

Zig-DbC Logo

Zig-DbC

Tests CodeFactor Zig Version Docs Release License

A Design by Contract Library for Zig


Zig-DbC is a small library that provides a collection of functions to use design by contract (DbC) principles in Zig programs. It provides a simple and idiomatic API for defining preconditions, postconditions, and invariants that can be checked at runtime.

A common use case for DbC (and by extension Zig-DbC) is adding checks that guarantee the code behaves as intended. This can be especially useful during the implementation of complex data structures and algorithms (like balanced trees and graphs) where correctness depends on specific conditions being met.

Features

  • A simple API to define preconditions, postconditions, and invariants
    • require and ensure functions check preconditions and postconditions
    • requiref and ensuref functions check preconditions and postconditions with formatted error messages
    • requireCtx and ensureCtx functions check preconditions and postconditions with a context string
    • contract and contractWithErrorTolerance functions check invariants
  • Checks are active in Debug, ReleaseSafe, and ReleaseSmall build modes to catch bugs
  • In ReleaseFast mode, all checks are removed at compile time to remove overhead

Important

Zig-DbC is in early development, so bugs and breaking API changes are expected. Please use the issues page to report bugs or request features.


Getting Started

You can add Zig-DbC to your project and start using it by following the steps below.

Installation

Run the following command in the root directory of your project to download Zig-DbC:

zig fetch --save=dbc "https://github.com/habedi/zig-dbc/archive/<branch_or_tag>.tar.gz"

Replace <branch_or_tag> with the desired branch or tag, like main (for the development version) or v0.2.0 (for the latest release). This command will download zig-dbc and add it to Zig's global cache and update your project's build.zig.zon file.

Adding to Build Script

Next, modify your build.zig file to make zig-dbc available to your build target as a module.

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const exe = b.addExecutable(.{
        .name = "your-zig-program",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // 1. Get the dependency object from the builder
    const zig_dbc_dep = b.dependency("dbc", .{});

    // 2. Get Zig-DbC's top-level module
    const zig_dbc_module = zig_dbc_dep.module("dbc");

    // 3. Add the module to your executable so you can @import("dbc")
    exe.root_module.addImport("dbc", zig_dbc_module);

    b.installArtifact(exe);
}

Using Zig-DbC in Your Code

Finally, you can @import("dbc") and start using it in your Zig code.

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

pub fn MyStruct() type {
    return struct {
        const Self = @This();
        field: i32, is_ready: bool,

        // The invariant guarantees that the object's state is always valid.
        // It's checked automatically by the `contract` function.
        fn invariant(self: Self) void {
            dbc.require(.{ self.field > 0, "Field must always be positive" });
        }

        pub fn doSomething(self: *Self) !void {
            const old = .{ .field = self.field };
            return dbc.contract(self, old,
                struct {fn run(ctx: @TypeOf(old), s: *Self) !void {
                    // Precondition
                    dbc.require(.{ s.is_ready, "Struct not ready" });

                    // ... method logic ...
                    s.field += 1;

                    // Postcondition
                    dbc.ensure(.{ s.field > ctx.field, "Field must increase" });
                }}.run);
        }
    };
}

Documentation

You can find the API documentation for the latest release of Zig-DbC here.

Alternatively, you can use the make docs command to generate the documentation for the current version of Zig-DbC. This will generate HTML documentation in the docs/api directory, which you can serve locally with make serve-docs and view in a web browser.

Validators

Zig-DbC supports reusable validators in require and ensure functions by passing as argument either:

  • a function with signature fn(T) bool, or
  • a struct value with a run function with this signature: pub fn run(self, T) bool.

Examples

Check out the examples directory for example usages of Zig-DbC.


Contributing

See CONTRIBUTING.md for details on how to make a contribution.

License

Zig-DbC is licensed under the MIT License (see LICENSE).

Acknowledgements

  • The chain links logo is from SVG Repo.