I’m pretty sure I already know the answer, but wanted to throw this out there as a last ditch. Let’s say I have an enum that can have different fields depending on some compile time known configuration. A working example of how I could do this is as follows:
const std = @import("std");
const comptime_option = true;
const SomeEnum = if (comptime_option) enum(u32) {
a = 0,
b,
c,
d,
pub fn someMethod(self: SomeEnum) u32 {
return @intFromEnum(self);
}
} else enum(u32) {
// Different ints for each value!
a = 0,
c,
d,
b,
// More values!
x,
z,
pub fn someMethod(self: SomeEnum) u32 {
return @intFromEnum(self);
}
};
pub fn main() void {
std.debug.print("Works: {d}\n", .{SomeEnum.a.someMethod()});
std.debug.print("Compile Error!: {d}\n", .{SomeEnum.x.someMethod()});
}
This accomplishes what I’m after, however you’ll notice the repeated code in both arms of the enum definition:
pub fn someMethod(self: SomeEnum) u32 {
return @intFromEnum(self);
}
Any way to avoid this repeated method definition or is this sorta just the way it is?
I’ll wait for other answers to roll in, but this post from @AndrewCodeDev coincidentally sort of answered my question! Here is a decent way to avoid code duplication when you have many methods that might be rather long:
const std = @import("std");
const comptime_option = true;
const SomeEnumImpl = struct {
pub fn someMethod(self: SomeEnum) u32 {
return @intFromEnum(self);
}
pub fn otherMethod(self: SomeEnum) u32 {
return @intFromEnum(self) * 2;
}
};
const SomeEnum = if (comptime_option) enum(u32) {
a = 0,
b,
c,
d,
pub const someMethod = SomeEnumImpl.someMethod;
pub const otherMethod = SomeEnumImpl.otherMethod;
} else enum(u32) {
// Different ints for each value!
a = 0,
c,
d,
b,
// More values!
x,
z,
pub const someMethod = SomeEnumImpl.someMethod;
pub const otherMethod = SomeEnumImpl.otherMethod;
};
pub fn main() void {
std.debug.print("Works: {d}\n", .{SomeEnum.b.someMethod()});
std.debug.print("Compile Error!: {d}\n", .{SomeEnum.x.someMethod()});
}
You duplicate code by defining the public const
members for each method, but is a lot more compact than duplicating the entire method code.
2 Likes
At the moment we don’t have a way to attach decls to a type created though @Type()
, so the best you can do is avoiding redeclaration of methods using Andrew’s method.
2 Likes
The eloquent way to get this is usingnamespace
.
fn EnumImpls(E: type) type {
return struct {
pub fn methodA(self: E, ...) void {
// ...
}
// etc
};
}
const SomeEnum = if (comptime_thing) enum(u32) {
fee,
fie,
foe,
pub usingnamespace EnumImpls(@This());
} else enum(u32) {
other,
tags,
here,
pub usingnamespace EnumImpls(@This());
};
Single source of truth.
1 Like
The more I look into it, the more I am torn on usingnamespace
- have we opened a thread dedicated to this topic yet? I think we should if we haven’t.
1 Like
I don’t think so, and that might be a good idea. The issue on the tracker has seen a lot of action, a discussion here would end up fairly high signal, that’s likely to be a net positive contribution.
I’ll encourage you to start a brainstorm when you find time, you would do a good job of being evenhanded about the pros and cons. I’m decidedly pro, wouldn’t want to put my thumb on the scale as the first post on the topic.
1 Like