Hello!
I’m learning zig by building a server that implements a binary protocol and I would like some thoughts on how I should design the payload abstraction.
To summarize, I need:
- structs that represent data that will be sent or received.
- functions to serialize and unserialize these structs, as well as some auxiliary ones.
- an association of struct type to an opcode.
So, my first attempt to implement this (which I’m very discomfortable about) consists of having an enumerated union where each field is a payload type.
Here is a skeleton of how I made it:
pub const Payload = union(enum(u16)) {
PayloadType1: struct {
f1: u16,
f2: u32,
// ...
} = 1,
PayloadType2: struct {
f1: f32,
f2: [8]u8,
// ...
} = 5,
// ...
pub fn serialize(self: Payload, allocator: mem.Allocator) ![]u8 {
switch (self) {
inline else => |payload| {
/// The actual implementation here
}
}
and it’s used like that:
const payload = Payload{.PayloadType1 = .{
.f1 = somethig,
.f2 = something_else,
}};
const serialized_payload = try payload.serialize(allocator);
defer allocator.free(serialized_payload);
send(p); // Just a simplification here
But I’m not really convinced that this is the best way to do that, as there’s some things that are bothering me:
- I have to do a static dispatch to do everything I need about the payload. i.e. every method I put behind
Payloadwill need to have that same pattern, which costs 2 indentation blocks for the function logic (or making these public methods just a wrapper, but I don’t like it that much). - I’m not really using any union feature other than the opcode to type association thing.
Given that, I would love to hear suggestions about how I can improve the abstraction of this code.
Thanks!