Skip to content

Instantly share code, notes, and snippets.

@andreadipersio
Last active June 12, 2022 19:01
Show Gist options
  • Save andreadipersio/2f8f4599d6924cd2b94f952805a0d3bc to your computer and use it in GitHub Desktop.
Save andreadipersio/2f8f4599d6924cd2b94f952805a0d3bc to your computer and use it in GitHub Desktop.
Zig Examples
const std = @import("std");
const io = std.io;
const assert = std.debug.assert;
// This example shows one possible way to approach Polymorphism in Zig
// using dynamic dispatch.
//
// Largely inspired by:
// - std/mem/Allocator.zig
// https://github.com/ziglang/zig/blob/77836e08a2384450b5e7933094511b61e3c22140/lib/std/mem/Allocator.zig#L55
//
// - the allocgate article:
// https://pithlessly.github.io/allocgate.html
//
// For more in depth examples:
// https://revivalizer.xyz/post/the-missing-zig-polymorphism-reference/
pub const Dog = struct {
ptr: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
bark: fn (ptr: *anyopaque) anyerror!void,
};
pub fn init(
pointer: anytype,
comptime barkFn: fn (ptr: @TypeOf(pointer)) anyerror!void,
) Dog {
const Ptr = @TypeOf(pointer);
const ptr_info = @typeInfo(Ptr);
assert(ptr_info == .Pointer);
assert(ptr_info.Pointer.size == .One);
const alignment = ptr_info.Pointer.alignment;
const gen = struct {
fn barkImpl(ptr: *anyopaque) anyerror!void {
const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
return @call(.{ .modifier = .always_inline }, barkFn, .{self});
}
const vtable = VTable{
.bark = barkImpl,
};
};
return .{
.ptr = pointer,
.vtable = &gen.vtable,
};
}
pub fn bark(self: Dog) anyerror!void {
return self.vtable.bark(self.ptr);
}
};
pub const Pug = struct {
name: []const u8,
pub fn dog(self: *Pug) Dog {
return Dog.init(self, bark);
}
fn bark(self: *Pug) anyerror!void {
std.debug.print("{s}: Derp Derp!\n", .{self.name});
}
};
pub const Husky = struct {
name: []const u8,
pub fn dog(self: *Husky) Dog {
return Dog.init(self, bark);
}
fn bark(self: *Husky) anyerror!void {
std.debug.print("{s}: Woof Woof!\n", .{self.name});
}
};
fn bark_twice(doggo: *Dog) anyerror!void {
try doggo.bark();
try doggo.bark();
}
pub fn main() anyerror!void {
var pug = Pug{ .name = "Frank" };
var husky = Husky{ .name = "Saskia" };
try bark_twice(&pug.dog());
try bark_twice(&husky.dog());
}
const std = @import("std");
const io = std.io;
const testing = std.testing;
// The goal of this example is being able to do dependency injection of
// Stdin and Stdout.
// When writing tests we can replace Stdin and Stdout with buffers.
// Stdin -> Write to a buffer
// Stdout -> Read from a buffer
pub const EchoMachine = struct {
//
// io.StreamSource allow us to use either a file or a buffer
//
in: io.StreamSource.Reader,
out: io.StreamSource.Writer,
pub fn echo(self: EchoMachine) !void {
var buf: [64]u8 = undefined;
const user_input = try self.in.readUntilDelimiterOrEof(&buf, '\n');
try self.out.print("{s}\n", .{user_input});
}
};
pub fn main() anyerror!void {
var em = EchoMachine{
//
// In the command line implementation we use Stdin and Stdout
//
.in = (io.StreamSource{ .file = io.getStdIn() }).reader(),
.out = (io.StreamSource{ .file = io.getStdOut() }).writer(),
};
try em.echo();
}
test "test the echo machine" {
var in_buf: [64]u8 = undefined;
var out_buf: [64]u8 = undefined;
var in_stream = io.fixedBufferStream(&in_buf);
var out_stream = io.fixedBufferStream(&out_buf);
var em = EchoMachine{
//
// In our test we use buffers instead
//
.in = (io.StreamSource{ .buffer = in_stream }).reader(),
.out = (io.StreamSource{ .buffer = out_stream }).writer(),
};
const expected = "This is a test!\n";
try in_stream.writer().writeAll(expected);
try em.echo();
var result_buf: [64]u8 = undefined;
_ = try out_stream.reader().readAll(&result_buf);
try testing.expectEqualSlices(u8, expected, result_buf[0..expected.len]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment