说明
rust在实现builder模式的时候,可以直接通过过程宏,直接通过修改token流,自动实现Builder类,同样的,zig也可以通过其特有的comptime生成Builder类。
代码实现
实现一个简单的泛型函数,接受需要实现Bulider类的type,返回对应的Builder类。
const std = @import("std");
/// A generic builder that constructs a value of type T step by step
pub fn Builder(comptime T: type) type {
return struct {
value: T = std.mem.zeroes(T),
const Self = @This();
/// Initialize the builder with default values
pub fn init() Self {
return .{};
}
/// Set a field by name and value
pub fn set(self: *Self, comptime field: []const u8, value: anytype) *Self {
if (!@hasField(T, field)) {
@compileError(std.fmt.comptimePrint("Type {s} has no field named '{s}'", .{ @typeName(T), field }));
}
const field_type = @TypeOf(@field(self.value, field));
if (@TypeOf(value) != field_type) {
@compileError(std.fmt.comptimePrint("Expected type {s} for field '{s}', got {s}", .{
@typeName(field_type),
field,
@typeName(@TypeOf(value)),
}));
}
@field(self.value, field) = value;
return self;
}
/// Build and return the final value
pub fn build(self: *const Self) T {
return self.value;
}
};
}
// Example usage:
const Person = struct {
name: []const u8 = "",
age: u8 = 0,
active: bool = false,
};
test "Builder example" {
var person_builder = Builder(Person){};
const name: []const u8 = "Alice";
_ = person_builder.set("name", name).set("age", @as(u8, 30)).set("active", true);
const person = person_builder.build();
try std.testing.expectEqualStrings("Alice", person.name);
try std.testing.expectEqual(@as(u8, 30), person.age);
try std.testing.expect(person.active);
}
test "Builder with default values" {
var person_builder = Builder(Person){};
const name: []const u8 = "Bob";
_ = person_builder.set("name", name);
const person = person_builder.build();
try std.testing.expectEqualStrings("Bob", person.name);
try std.testing.expectEqual(@as(u8, 0), person.age); // default value
try std.testing.expect(!person.active); // default value
}