Awesome
go-enum-encoding
go install github.com/nikolaydubina/go-enum-encoding@latest
type Color struct{ c uint8 }
//go:generate go-enum-encoding -type=Color
var (
UndefinedColor = Color{} // json:""
Red = Color{1} // json:"red"
Green = Color{2} // json:"green"
Blue = Color{3} // json:"blue"
)
It also works with raw iota
enums:
type Size uint8
//go:generate go-enum-encoding -type=Size
const (
UndefinedSize Size = iota // json:""
Small // json:"small"
Large // json:"large"
XLarge // json:"xlarge"
)
Related Work and References
- http://github.com/zarldev/goenums - does much more advanced struct generation, generates all enum utilities besides encoding, does not generate tests, uses similar notation to trigger go:generate but with different comment directives (non-json field tags)
@mishak87 proposed to use array instead of map for performance. Similarly, @nikolaydubina faced degradation in performance for loop based array for large enum sets (256 values) while working on fpmoney2 and iso42173.
$ go test -bench=Benchmark -benchmem ./internal/research/map > map.bench
$ go test -bench=Benchmark -benchmem ./internal/research/inline > inline.bench
$ go test -bench=Benchmark -benchmem ./internal/research/array-loop > array-loop.bench
$ go test -bench=Benchmark -benchmem ./internal/research/array-index > array-index.bench
$ go test -bench=Benchmark -benchmem ./internal/research/uint-array > uint-array.bench
$ go test -bench=Benchmark -benchmem ./internal/research/uint-inline > uint-inline.bench
$ benchstat -split="XYZ" map.bench inline.bench array-loop.bench array-index.bench uint-array.bench uint-inline.bench
name \ time/op map.bench inline.bench array-loop.bench array-index.bench uint-array.bench uint-inline.bench
MarshalText_Color-16 22.3ns ± 0% 5.3ns ± 0% 7.5ns ± 0% 2.2ns ± 0% 1.9ns ± 0% 5.0ns ± 0%
UnmarshalText_Color-16 11.9ns ± 0% 5.7ns ± 0% 14.5ns ± 0% 11.8ns ± 0% 14.3ns ± 0% 5.7ns ± 0%
name \ alloc/op map.bench inline.bench array-loop.bench array-index.bench uint-array.bench uint-inline.bench
MarshalText_Color-16 0.00B 0.00B 0.00B 0.00B 0.00B 0.00B
UnmarshalText_Color-16 0.00B 0.00B 0.00B 0.00B 0.00B 0.00B
name \ allocs/op map.bench inline.bench array-loop.bench array-index.bench uint-array.bench uint-inline.bench
MarshalText_Color-16 0.00 0.00 0.00 0.00 0.00 0.00
UnmarshalText_Color-16 0.00 0.00 0.00 0.00 0.00 0.00
</details>
<details><summary>Appendix: Stringer</summary>
String() string
method is very similar to encoding, howver it does not return error and returns string
instead of []byte
.
To avoid malloc and convenience, stringer option is added to generate it accompanied with tests and benchmarks.
First used in tailscale.
Some enums are encoded directly as underlying basic type, however they have dual custom use through String() string
methods and alike.
To assist safe migration, custom encode and decode method names are added.
First used in tailscale.
Footnotes
-
Comparison to other enums methods: http://github.com/nikolaydubina/go-enum-example ↩ ↩2
-
iso4217 enums performance loop vs map: https://github.com/ferdypruis/iso4217/issues/4 ↩
-
fpmoney: https://github.com/nikolaydubina/fpmoney?tab=readme-ov-file#appendix-a-jsonunmarshal-optimizations ↩