Example adapted from, probably, the Programming in Ada 2012 book.
const std = @import("std");
const builtin = @import("builtin");
fn assert(ok: bool) void {
// negate the pop() precondition and see what happens without this test
if (builtin.mode == .ReleaseFast) return;
if (!ok) unreachable;
}
fn Stack(T: type, S: usize) type {
return struct {
stack: [S]T,
depth: usize,
pub const empty = Stack(T, S){ .stack = .{0} ** S, .depth = 0 };
pub fn is_empty(self: *Stack(T, S)) bool {
return self.depth == 0;
}
pub fn is_full(self: *Stack(T, S)) bool {
return self.depth == S;
}
pub fn pop(self: *Stack(T, S)) T {
assert(!self.is_empty());
self.depth -= 1;
return self.stack[self.depth];
}
pub fn push(self: *Stack(T, S), x: T) void {
assert(!self.is_full());
defer assert(!self.is_empty());
self.stack[self.depth] = x;
self.depth += 1;
}
pub fn slice(self: *Stack(T, S)) []T {
return self.stack[0..self.depth];
}
};
}
pub fn main() !void {
var a: Stack(u8, 5) = .empty;
var b: Stack(u8, 5) = .empty;
a.push(1);
b.push(1);
b.push(2);
try std.testing.expect(!std.mem.eql(u8, a.slice(), b.slice()));
_ = b.pop();
try std.testing.expect(std.mem.eql(u8, a.slice(), b.slice()));
b.push(2);
b.push(3);
b.push(4);
b.push(5);
try std.testing.expect(b.is_full());
std.debug.print("{any}\n{any}\n", .{ a, b });
}