GitHub - nikolaydubina/go-enum-example: Go Enum: benchmarks, examples, analysis (original) (raw)

How to make strict Enum in Go?

To harden Enum with,

Simply,

package color

type Color struct{ c uint }

var ( Undefined = Color{} Red = Color{1} Green = Color{2} Blue = Color{3} )

There are still uncovered cases, albeit they are very unlikely and easy to spot,

Why not string?

type Color string

const( Red Color = "red" Blue Color = "blue" )

Passing strings in Go is done by referneces. String content is not copied in function calls nor assignments.

Problems

Why not uint and iota?

type Color uint

const( Red Color = iota Blue )

This is very common way. It is also efficient.

Problems,

Benchmarks

All versions have zero mallocs and memory transfer.

go test -bench=. -count=3 ./color > doc/struct go test -bench=. -count=3 ./color-int > doc/int go test -bench=. -count=3 ./color-string > doc/string benchstat -split="XYZ" doc/struct doc/int doc/string

name \ time/op                          struct       int          string
EnumPassFunction/call_one-10            2.12ns ± 0%  2.14ns ± 0%   2.52ns ± 0%
EnumPassFunction/call_one_apple-10      2.37ns ± 0%  2.44ns ± 0%   8.62ns ± 0%
EnumPassFunction_Big/call_one-10        2.19ns ± 0%  2.14ns ± 0%   6.59ns ± 0%
EnumPassFunction_Big/call_one_apple-10  2.39ns ± 0%  2.38ns ± 0%  11.19ns ± 0%

What is Enum?

Enum is a data type consisting of a set of named values. They are typically constants. They have comaprison and assignemnt operators. Underlying represtentation is free up to compiler, but typically is an integer. Enums are typically prevented from illogical operations such as arithmetic operations.

References

Assembly

Overall, int and struct { int } versions are same and very efficient. Version with string, as expected, has much more code and jumps, presumably for string comparison logic with shortcuts.

int

main_callOne_pc101: CALL runtime.panicdivide(SB) XCHGL AX, AX TEXT main.callOneApple(SB), NOSPLIT|ABIInternal, $0-48 MOVQ BX, main.a+16(FP) FUNCDATA $0, gclocals·IuErl7MOXaHVn7EZYWzfFA==(SB) FUNCDATA $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB) FUNCDATA $5, main.callOneApple.arginfo1(SB) FUNCDATA $6, main.callOneApple.argliveinfo(SB) PCDATA 3,3, 3,1 CMPB SIB, R8B JNE main_callOneApple_pc23 XORL AX, AX XORL BX, BX MOVQ BX, CX MOVL $3, DI RET

struct

main_callOne_pc101: CALL runtime.panicdivide(SB) XCHGL AX, AX TEXT main.callOneApple(SB), NOSPLIT|ABIInternal, $0-48 MOVQ BX, main.a+16(FP) FUNCDATA $0, gclocals·IuErl7MOXaHVn7EZYWzfFA==(SB) FUNCDATA $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB) FUNCDATA $5, main.callOneApple.arginfo1(SB) FUNCDATA $6, main.callOneApple.argliveinfo(SB) PCDATA 3,3, 3,1 CMPB SIB, R8B JNE main_callOneApple_pc25 MOVBLZX main.Purple(SB), DI XORL AX, AX XORL BX, BX MOVQ BX, CX RET

string

main_callOneApple_pc0: TEXT main.callOneApple(SB), ABIInternal, $72-64 CMPQ SP, 16(R14) PCDATA 0,0, 0,-2 JLS main_callOneApple_pc234 PCDATA 0,0, 0,-1 SUBQ $72, SP MOVQ BP, 64(SP) LEAQ 64(SP), BP MOVQ R9, main.c+128(FP) FUNCDATA $0, gclocals·J+YAdREO0hCD8EYeU6UDCw==(SB) FUNCDATA $1, gclocals·yROwgZmxcEjQO7qZUR29ZQ==(SB) FUNCDATA $5, main.callOneApple.arginfo1(SB) FUNCDATA $6, main.callOneApple.argliveinfo(SB) PCDATA 3,3, 3,1 MOVQ BX, main.a+88(SP) MOVQ CX, main.a+96(SP) MOVQ DI, main.a+104(SP) MOVQ SI, main.a+112(SP) MOVQ R8, main.a+120(SP) MOVUPS X15, main.r0+24(SP) MOVUPS X15, main.r0+32(SP) MOVUPS X15, main.r0+48(SP) MOVQ main.a+120(SP), CX MOVQ main.a+112(SP), AX CMPQ R10, CX JNE main_callOneApple_pc105 MOVQ R9, BX PCDATA 1,1, 1,1 NOP CALL runtime.memequal(SB) TESTB AL, AL JNE main_callOneApple_pc170 main_callOneApple_pc105: MOVQ main.a+88(SP), DX MOVQ DX, main.r0+24(SP) MOVUPS main.a+96(SP), X0 MOVUPS X0, main.r0+32(SP) MOVUPS main.a+112(SP), X0 MOVUPS X0, main.r0+48(SP) MOVQ main.r0+24(SP), AX MOVQ main.r0+32(SP), BX MOVQ main.r0+40(SP), CX MOVQ main.r0+48(SP), DI MOVQ main.r0+56(SP), SI MOVQ 64(SP), BP ADDQ $72, SP RET main_callOneApple_pc170: MOVUPS X15, main.r0+24(SP) MOVUPS X15, main.r0+32(SP) MOVUPS X15, main.r0+48(SP) LEAQ go.string."purple"(SB), DI MOVQ DI, main.r0+48(SP) MOVQ $6, main.r0+56(SP) MOVQ main.~r0+24(SP), AX XORL BX, BX MOVQ BX, CX MOVL $6, SI MOVQ 64(SP), BP ADDQ $72, SP RET main_callOneApple_pc234: NOP PCDATA 1,1, 1,-1 PCDATA 0,0, 0,-2 MOVQ AX, 8(SP) MOVQ BX, 16(SP) MOVQ CX, 24(SP) MOVQ DI, 32(SP) MOVQ SI, 40(SP) MOVQ R8, 48(SP) MOVQ R9, 56(SP) MOVQ R10, 64(SP) CALL runtime.morestack_noctxt(SB) MOVQ 8(SP), AX MOVQ 16(SP), BX MOVQ 24(SP), CX MOVQ 32(SP), DI MOVQ 40(SP), SI MOVQ 48(SP), R8 MOVQ 56(SP), R9 MOVQ 64(SP), R10 PCDATA 0,0, 0,-1 NOP JMP main_callOneApple_pc0 main_main_pc0: TEXT main.main(SB), ABIInternal, $72-0 CMPQ SP, 16(R14) PCDATA 0,0, 0,-2 JLS main_main_pc88 PCDATA 0,0, 0,-1 SUBQ $72, SP MOVQ BP, 64(SP) LEAQ 64(SP), BP FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) MOVQ main..stmp_1(SB), BX MOVQ main..stmp_1+8(SB), CX MOVQ main..stmp_1+16(SB), DI MOVQ main..stmp_1+24(SB), SI MOVQ main..stmp_1+32(SB), R8 MOVL $10, AX LEAQ go.string."blue"(SB), R9 MOVL $4, R10 PCDATA 1,1, 1,0 CALL main.callOneApple(SB) MOVQ 64(SP), BP ADDQ $72, SP RET