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.
- A simple API to define preconditions, postconditions, and invariants
require
andensure
functions check preconditions and postconditionsrequiref
andensuref
functions check preconditions and postconditions with formatted error messagesrequireCtx
andensureCtx
functions check preconditions and postconditions with a context stringcontract
andcontractWithErrorTolerance
functions check invariants
- Checks are active in
Debug
,ReleaseSafe
, andReleaseSmall
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.
You can add Zig-DbC to your project and start using it by following the steps below.
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.
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);
}
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);
}
};
}
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.
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
.
Check out the examples directory for example usages of Zig-DbC.
See CONTRIBUTING.md for details on how to make a contribution.
Zig-DbC is licensed under the MIT License (see LICENSE).
- The chain links logo is from SVG Repo.