{"id":13720075,"url":"https://github.com/nikolaydubina/go-enum-example","last_synced_at":"2025-05-07T23:22:53.187Z","repository":{"id":97356322,"uuid":"565092559","full_name":"nikolaydubina/go-enum-example","owner":"nikolaydubina","description":"Go Enum: benchmarks, examples, analysis","archived":false,"fork":false,"pushed_at":"2024-05-09T04:52:48.000Z","size":64,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-20T04:34:06.187Z","etag":null,"topics":["assembly","benchmarking","enum","example","go","golang","research"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nikolaydubina.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-12T10:14:39.000Z","updated_at":"2025-04-02T21:18:39.000Z","dependencies_parsed_at":"2024-05-09T05:47:06.375Z","dependency_job_id":null,"html_url":"https://github.com/nikolaydubina/go-enum-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikolaydubina%2Fgo-enum-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikolaydubina%2Fgo-enum-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikolaydubina%2Fgo-enum-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikolaydubina%2Fgo-enum-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nikolaydubina","download_url":"https://codeload.github.com/nikolaydubina/go-enum-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252969055,"owners_count":21833403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly","benchmarking","enum","example","go","golang","research"],"created_at":"2024-08-03T01:00:59.550Z","updated_at":"2025-05-07T23:22:53.147Z","avatar_url":"https://github.com/nikolaydubina.png","language":"Go","funding_links":[],"categories":["Static Analysis"],"sub_categories":["[⏫](#contents) Rely on compiler for stricter Enums"],"readme":"### How to make strict Enum in Go?\n\nTo harden Enum with,\n* compile time block of accidental arithmetics\n* compile time block of implicit cast of untyped constants\n* compile time block of all operators except `==` and `!=`\n* compile time block of creating new values\n* **zero overhead**\n\nSimply,\n* wrap into struct\n* do not export field\n* make separate package\n\n```go\npackage color\n\ntype Color struct{ c uint }\n\nvar (\n\tUndefined = Color{}\n\tRed       = Color{1}\n\tGreen     = Color{2}\n\tBlue      = Color{3}\n)\n```\n\nThere are still uncovered cases, albeit they are very unlikely and easy to spot,\n- outside of package can swap enum values\n- (if not separate pacakge) inside of package can override values\n\n### Why not `string`?\n\n```go\ntype Color string\n\nconst(\n\tRed Color = \"red\"\n\tBlue Color = \"blue\"\n)\n```\n\nPassing strings in Go is done by referneces.\nString content is not copied in function calls nor assignments.\n\nProblems\n * string comparison can take up to `O(N)`, which is very common operation on Enums\n * strings in struct fields result to more indirection as compared to `int` or `struct`\n * if it is not wrapped into struct, it will also leak comparison and concatenation operators and casts\n * can be up to `4x` ~ `5x` slower\n\n\n### Why not `uint` and `iota`?\n\n```go\ntype Color uint\n\nconst(\n\tRed Color = iota\n\tBlue\n)\n```\n\nThis is very common way.\nIt is also efficient.\n\nProblems,\n * leaks all arithmetic operators\n * leaks cast\n * easily mixed with untyped constants in very many places: function returns, channles, switch, casts, assignemnts, etc.\n\n### Benchmarks\n\nAll versions have zero mallocs and memory transfer.\n\n```bash\ngo test -bench=. -count=3 ./color \u003e doc/struct\ngo test -bench=. -count=3 ./color-int \u003e doc/int\ngo test -bench=. -count=3 ./color-string \u003e doc/string\nbenchstat -split=\"XYZ\" doc/struct doc/int doc/string\n```\n\n```\nname \\ time/op                          struct       int          string\nEnumPassFunction/call_one-10            2.12ns ± 0%  2.14ns ± 0%   2.52ns ± 0%\nEnumPassFunction/call_one_apple-10      2.37ns ± 0%  2.44ns ± 0%   8.62ns ± 0%\nEnumPassFunction_Big/call_one-10        2.19ns ± 0%  2.14ns ± 0%   6.59ns ± 0%\nEnumPassFunction_Big/call_one_apple-10  2.39ns ± 0%  2.38ns ± 0%  11.19ns ± 0%\n```\n\n### What is Enum?\n\nEnum is a data type consisting of a set of named values.\nThey are typically constants.\nThey have comaprison and assignemnt operators.\nUnderlying represtentation is free up to compiler, but typically is an integer.\nEnums are typically prevented from illogical operations such as arithmetic operations.\n\n* `C` - interger; exposed; arithmetics permitted;\n* `C#` - integer; not exposed; some arithmetics permitted;\n* `C++` - integer; not exposed; not converted; some arithmetics not permitted;\n* `Go` - integer; exposed; arithmetics permitted;\n* `Python` - integer; arithmetics permitted;\n* `Java` - not integer; not converted; arithmetics not permitted; internally integer;\n* `Rust` - not integer; not converted; arithmetics not permitted; can be extended to integer\n* `Swift` - not integer; not converted; arithmetics not permitted; can be extended to interger\n\n### Related Tools\n\n* https://github.com/nishanths/exhaustive - performs exhaustive checks in switch; does not check for type conversions; widely used; however does not support struct enums\n* https://github.com/loov/enumcheck - focuses on performing exhastive checks; also does constant expressions validations; as of 2022-11-15, work in progress after 3 years\n\n### References\n\n* https://en.wikipedia.org/wiki/Enumerated_type\n* https://go.dev/ref/spec\n* https://www.w3schools.com/java/java_enums.asp\n* https://en.cppreference.com/w/cpp/language/enum\n* https://en.cppreference.com/w/c/language/enum\n* https://docs.python.org/3/library/enum.html\n* https://godbolt.org\n\n### Assembly\n\nOverall, `int` and `struct { int }` versions are same and very efficient.\nVersion with `string`, as expected, has much more code and jumps, presumably for string comparison logic with shortcuts.\n\n`int`\n\n```s\nmain_callOne_pc101:\n        CALL    runtime.panicdivide(SB)\n        XCHGL   AX, AX\n        TEXT    main.callOneApple(SB), NOSPLIT|ABIInternal, $0-48\n        MOVQ    BX, main.a+16(FP)\n        FUNCDATA        $0, gclocals·IuErl7MOXaHVn7EZYWzfFA==(SB)\n        FUNCDATA        $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)\n        FUNCDATA        $5, main.callOneApple.arginfo1(SB)\n        FUNCDATA        $6, main.callOneApple.argliveinfo(SB)\n        PCDATA  $3, $1\n        CMPB    SIB, R8B\n        JNE     main_callOneApple_pc23\n        XORL    AX, AX\n        XORL    BX, BX\n        MOVQ    BX, CX\n        MOVL    $3, DI\n        RET\n```\n\n`struct`\n\n```s\nmain_callOne_pc101:\n        CALL    runtime.panicdivide(SB)\n        XCHGL   AX, AX\n        TEXT    main.callOneApple(SB), NOSPLIT|ABIInternal, $0-48\n        MOVQ    BX, main.a+16(FP)\n        FUNCDATA        $0, gclocals·IuErl7MOXaHVn7EZYWzfFA==(SB)\n        FUNCDATA        $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)\n        FUNCDATA        $5, main.callOneApple.arginfo1(SB)\n        FUNCDATA        $6, main.callOneApple.argliveinfo(SB)\n        PCDATA  $3, $1\n        CMPB    SIB, R8B\n        JNE     main_callOneApple_pc25\n        MOVBLZX main.Purple(SB), DI\n        XORL    AX, AX\n        XORL    BX, BX\n        MOVQ    BX, CX\n        RET\n```\n\n`string`\n```s\nmain_callOneApple_pc0:\n        TEXT    main.callOneApple(SB), ABIInternal, $72-64\n        CMPQ    SP, 16(R14)\n        PCDATA  $0, $-2\n        JLS     main_callOneApple_pc234\n        PCDATA  $0, $-1\n        SUBQ    $72, SP\n        MOVQ    BP, 64(SP)\n        LEAQ    64(SP), BP\n        MOVQ    R9, main.c+128(FP)\n        FUNCDATA        $0, gclocals·J+YAdREO0hCD8EYeU6UDCw==(SB)\n        FUNCDATA        $1, gclocals·yROwgZmxcEjQO7qZUR29ZQ==(SB)\n        FUNCDATA        $5, main.callOneApple.arginfo1(SB)\n        FUNCDATA        $6, main.callOneApple.argliveinfo(SB)\n        PCDATA  $3, $1\n        MOVQ    BX, main.a+88(SP)\n        MOVQ    CX, main.a+96(SP)\n        MOVQ    DI, main.a+104(SP)\n        MOVQ    SI, main.a+112(SP)\n        MOVQ    R8, main.a+120(SP)\n        MOVUPS  X15, main.~r0+24(SP)\n        MOVUPS  X15, main.~r0+32(SP)\n        MOVUPS  X15, main.~r0+48(SP)\n        MOVQ    main.a+120(SP), CX\n        MOVQ    main.a+112(SP), AX\n        CMPQ    R10, CX\n        JNE     main_callOneApple_pc105\n        MOVQ    R9, BX\n        PCDATA  $1, $1\n        NOP\n        CALL    runtime.memequal(SB)\n        TESTB   AL, AL\n        JNE     main_callOneApple_pc170\nmain_callOneApple_pc105:\n        MOVQ    main.a+88(SP), DX\n        MOVQ    DX, main.~r0+24(SP)\n        MOVUPS  main.a+96(SP), X0\n        MOVUPS  X0, main.~r0+32(SP)\n        MOVUPS  main.a+112(SP), X0\n        MOVUPS  X0, main.~r0+48(SP)\n        MOVQ    main.~r0+24(SP), AX\n        MOVQ    main.~r0+32(SP), BX\n        MOVQ    main.~r0+40(SP), CX\n        MOVQ    main.~r0+48(SP), DI\n        MOVQ    main.~r0+56(SP), SI\n        MOVQ    64(SP), BP\n        ADDQ    $72, SP\n        RET\nmain_callOneApple_pc170:\n        MOVUPS  X15, main.~r0+24(SP)\n        MOVUPS  X15, main.~r0+32(SP)\n        MOVUPS  X15, main.~r0+48(SP)\n        LEAQ    go.string.\"purple\"(SB), DI\n        MOVQ    DI, main.~r0+48(SP)\n        MOVQ    $6, main.~r0+56(SP)\n        MOVQ    main.~r0+24(SP), AX\n        XORL    BX, BX\n        MOVQ    BX, CX\n        MOVL    $6, SI\n        MOVQ    64(SP), BP\n        ADDQ    $72, SP\n        RET\nmain_callOneApple_pc234:\n        NOP\n        PCDATA  $1, $-1\n        PCDATA  $0, $-2\n        MOVQ    AX, 8(SP)\n        MOVQ    BX, 16(SP)\n        MOVQ    CX, 24(SP)\n        MOVQ    DI, 32(SP)\n        MOVQ    SI, 40(SP)\n        MOVQ    R8, 48(SP)\n        MOVQ    R9, 56(SP)\n        MOVQ    R10, 64(SP)\n        CALL    runtime.morestack_noctxt(SB)\n        MOVQ    8(SP), AX\n        MOVQ    16(SP), BX\n        MOVQ    24(SP), CX\n        MOVQ    32(SP), DI\n        MOVQ    40(SP), SI\n        MOVQ    48(SP), R8\n        MOVQ    56(SP), R9\n        MOVQ    64(SP), R10\n        PCDATA  $0, $-1\n        NOP\n        JMP     main_callOneApple_pc0\nmain_main_pc0:\n        TEXT    main.main(SB), ABIInternal, $72-0\n        CMPQ    SP, 16(R14)\n        PCDATA  $0, $-2\n        JLS     main_main_pc88\n        PCDATA  $0, $-1\n        SUBQ    $72, SP\n        MOVQ    BP, 64(SP)\n        LEAQ    64(SP), BP\n        FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)\n        FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)\n        MOVQ    main..stmp_1(SB), BX\n        MOVQ    main..stmp_1+8(SB), CX\n        MOVQ    main..stmp_1+16(SB), DI\n        MOVQ    main..stmp_1+24(SB), SI\n        MOVQ    main..stmp_1+32(SB), R8\n        MOVL    $10, AX\n        LEAQ    go.string.\"blue\"(SB), R9\n        MOVL    $4, R10\n        PCDATA  $1, $0\n        CALL    main.callOneApple(SB)\n        MOVQ    64(SP), BP\n        ADDQ    $72, SP\n        RET\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnikolaydubina%2Fgo-enum-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnikolaydubina%2Fgo-enum-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnikolaydubina%2Fgo-enum-example/lists"}