Working with bitmasks in Go (Go enumeration with iota) - go

Bit stuck.
I'm trying to work out how to get the bitmask values from a const with go enumeration. For example, if key is 5, which is 0101 bits, it would be dog and fish. What is the easiest way to get the bit values (1,2,4,8,16, 32, 64 etc) so I can select the string values and return the set of animals?
type Key int
const (
Dog Key = 1 << iota
Cat
Fish
Horse
Snake
Rabbit
Lion
Rino
Hedgehog
)
Been reading but I can't work this out.

Declare a slice of strings where the strings correspond to the constant names:
var animalNames = []string{
"Dog",
"Cat",
"Fish",
"Horse",
"Snake",
"Rabbit",
"Lion",
"Rino",
"Hedgehog",
}
To get the names for the bits, loop through the names. If the corresponding bit is set, add the name to the result:
func Names(k Key) []string {
var result []string
for i := 0; i < len(animalNames); i++ {
if k&(1<<uint(i)) != 0 {
result = append(result, animalNames[i])
}
}
return result
}
Run it on the playground.
If you change the the constants to be bit indexes instead of bit values, then you can use the stringer utility to create a func (k Key) String() string. Here's the code for this change:
type Key uint
const (
Dog Key = iota
Cat
Fish
Horse
Snake
Rabbit
Lion
Rino
Hedgehog
)
//go:generate stringer -type=Key
func Names(k Key) []string {
var result []string
for animal := Dog; animal <= Hedgehog; animal++ {
if k&(1<<animal) != 0 {
result = append(result, animal.String())
}
}
return result
}

Creating bitmask values with iota
Iota can be very useful when creating a bitmask. For instance, to represent the state of a network connection which may be secure, authenticated, and/or ready, we might create a bitmask like the following:
package main
import "fmt"
const (
Secure = 1 << iota // 0b001
Authn // 0b010
Ready // 0b100
)
// 0b011: Connection is secure and authenticated, but not yet Ready
func main() {
ConnState := Secure | Authn
fmt.Printf(` Secure: 0x%x (0b%03b)
Authn: 0x%x (0b%03b)
ConnState: 0x%x (0b%03b)
`, Secure, Secure, Authn, Authn, ConnState, ConnState)
}

Related

How to convert a struct into a flat array of paths?

If for example I have a struct such as:
type Example struct {
Foo string
Bar string
Baz struct{
A int
B string
}
Qux []string
}
What would be the best approach to convert it into a flat slice of strings representing the dot path to each struct field?
I want an output that looks like the following slice:
["Example.Foo", "Example.Bar", "Example.Baz.A", "Example.Baz.B", "Example.Qux.0", "Example.Qux.1"]
The exact structs will be known at compile time. Also, the conversion from struct to a flat list is in a hot path so performance will be an important consideration.
Any hints would be appreciated!
You have to code it yourself, with reflection.
This is a demonstrative function that prints the output you provided:
package main
import (
"fmt"
"reflect"
"strconv"
)
type Example struct {
Foo string
Bar string
Baz struct{
A int
B string
}
Qux []string
}
func main() {
example := Example{Qux: []string{"a", "b"}}
t := reflect.ValueOf(example)
prefix := t.Type().Name()
fmt.Println(ToPathSlice(t, prefix, make([]string, 0)))
}
func ToPathSlice(t reflect.Value, name string, dst []string) []string {
switch t.Kind() {
case reflect.Ptr, reflect.Interface:
return ToPathSlice(t.Elem(), name, dst)
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
fname := t.Type().Field(i).Name
dst = ToPathSlice(t.Field(i), name+"."+fname, dst)
}
case reflect.Slice, reflect.Array:
for i := 0; i < t.Len(); i++ {
dst = ToPathSlice(t.Index(i), name+"."+strconv.Itoa(i), dst)
}
default:
return append(dst, name)
}
return dst
}
Will print:
[Example.Foo Example.Bar Example.Baz.A Example.Baz.B Example.Qux.0 Example.Qux.1]
Note that:
reflection comes at a performance penalty; if you are concerned about this, you should profile the relevant code path to see if it's a deal breaker
the above code is contrived, for example it doesn't handle maps, it doesn't handle nil, etc.; you can expand it yourself
in your desired output, the indices of slice/array fields is printed. Slices don't have inherent length as arrays. In order to know the length of slices, you have to work with reflect.Value. This IMO makes the code more awkward. If you can accept not printing indices for slices, then you can work with reflect.Type.
Playground: https://play.golang.org/p/isNFSfFiXOP

How to access unexported struct fields

Is there a way to use reflect to access unexported fields in Go 1.8?
This no longer seems to work: https://stackoverflow.com/a/17982725/555493
Note that reflect.DeepEqual works just fine (that is, it can access unexported fields) but I can't make heads or tails of that function. Here's a go playarea that shows it in action: https://play.golang.org/p/vyEvay6eVG. The src code is below
import (
"fmt"
"reflect"
)
type Foo struct {
private string
}
func main() {
x := Foo{"hello"}
y := Foo{"goodbye"}
z := Foo{"hello"}
fmt.Println(reflect.DeepEqual(x,y)) //false
fmt.Println(reflect.DeepEqual(x,z)) //true
}
If the struct is addressable, you can use unsafe.Pointer to access the field (read or write) it, like this:
rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf can't be read or set.
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read and set.
See full example on the playground.
This use of unsafe.Pointer is valid according to the documentation and running go vet returns no errors.
If the struct is not addressable this trick won't work, but you can create an addressable copy like this:
rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read. Setting will succeed but only affects the temporary copy.
See full example on the playground.
Based on cpcallen's work:
import (
"reflect"
"unsafe"
)
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
reflect.NewAt might be confusing to read at first. It returns a reflect.Value representing a pointer to a value of the specified field.Type(), using unsafe.Pointer(field.UnsafeAddr()) as that pointer. In this context reflect.NewAt is different than reflect.New, which would return a pointer to a freshly initialized value.
Example:
type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
https://play.golang.org/p/IgjlQPYdKFR
reflect.DeepEqual() can do it because it has access to unexported features of the reflect package, in this case namely for the valueInterface() function, which takes a safe argument, which denies access to unexported field values via the Value.Interface() method if safe=true. reflect.DeepEqual() will (might) call that passing safe=false.
You can still do it, but you cannot use Value.Interface() for unexported fields. Instead you have to use type-specific methods, such as Value.String() for string, Value.Float() for floats, Value.Int() for ints etc. These will return you a copy of the value (which is enough to inspect it), but will not allow you to modify the field's value (which might be "partly" possible if Value.Interface() would work and the field type would be a pointer type).
If a field happens to be an interface type, you may use Value.Elem() to get to the value contained / wrapped by the interface value.
To demonstrate:
type Foo struct {
s string
i int
j interface{}
}
func main() {
x := Foo{"hello", 2, 3.0}
v := reflect.ValueOf(x)
s := v.FieldByName("s")
fmt.Printf("%T %v\n", s.String(), s.String())
i := v.FieldByName("i")
fmt.Printf("%T %v\n", i.Int(), i.Int())
j := v.FieldByName("j").Elem()
fmt.Printf("%T %v\n", j.Float(), j.Float())
}
Output (try it on the Go Playground):
string hello
int64 2
float64 3
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type Person1 struct {
W3ID string
Name string
}
type Address1 struct {
city string
country string
}
type User1 struct {
name string
age int
address Address1
manager Person1
developer Person1
tech Person1
}
func showDetails(load, email interface{}) {
if reflect.ValueOf(load).Kind() == reflect.Struct {
typ := reflect.TypeOf(load)
value := reflect.ValueOf(load)
//#1 For struct, not addressable create a copy With Element.
value2 := reflect.New(value.Type()).Elem()
//#2 Value2 is addressable and can be set
value2.Set(value)
for i := 0; i < typ.NumField(); i++ {
if value.Field(i).Kind() == reflect.Struct {
rf := value2.Field(i)
/* #nosec G103 */
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
irf := rf.Interface()
typrf := reflect.TypeOf(irf)
nameP := typrf.String()
if strings.Contains(nameP, "Person") {
//fmt.Println(nameP, "FOUND !!!!!!! ")
for j := 0; j < typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, "W3ID") {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, " MATCH!!!!")
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf("%d.Type:%T || Value:%#v\n",
// (i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := "tsumi#in.org.com"
load := User1{
name: "John Doe",
age: 34,
address: Address1{
city: "New York",
country: "USA",
},
manager: Person1{
W3ID: "jBult#in.org.com",
Name: "Bualt",
},
developer: Person1{
W3ID: "tsumi#in.org.com",
Name: "Sumi",
},
tech: Person1{
W3ID: "lPaul#in.org.com",
Name: "Paul",
},
}
showDetails(load, iD)
}

Golang: convert struct to embedded at offset 0 struct

I have some different structs like Big with Small embedded at offset 0.
How can I access Small's structure fields from code, that doesn't know anything about Big type, but it is known that Small is at offset 0?
type Small struct {
val int
}
type Big struct {
Small
bigval int
}
var v interface{} = Big{}
// here i only know about 'Small' struct and i know that it is at the begining of variable
v.(Small).val // compile error
It seems that compiler is theoretically able to operate such expression, because it knows that Big type has Small type embedded at offset 0. Is there any way to do such things (maybe with unsafe.Pointer)?
While answer with reflection is working but it has performance penalties and is not idiomatic to Go.
I believe you should use interface. Like this
https://play.golang.org/p/OG1MPHjDlQ
package main
import (
"fmt"
)
type MySmall interface {
SmallVal() int
}
type Small struct {
val int
}
func (v Small) SmallVal() int {
return v.val
}
type Big struct {
Small
bigval int
}
func main() {
var v interface{} = Big{Small{val: 3}, 4}
fmt.Printf("Small val: %v", v.(MySmall).SmallVal())
}
Output:
Small val: 3
Avoid using unsafe whenever possible. The above task can be done using reflection (reflect package):
var v interface{} = Big{Small{1}, 2}
rf := reflect.ValueOf(v)
s := rf.FieldByName("Small").Interface()
fmt.Printf("%#v\n", s)
fmt.Printf("%#v\n", s.(Small).val)
Output (try it on the Go Playground):
main.Small{val:1}
1
Notes:
This works for any field, not just the first one (at "offset 0"). This also works for named fields too, not just for embedded fields. This doesn't work for unexported fields though.
type Small struct {
val int
}
type Big struct {
Small
bigval int
}
func main() {
var v = Big{Small{10},200}
print(v.val)
}

Golang: declaring maps and slices with iota values

I have this Go code:
package main
import "fmt"
type baseGroup int
const (
fooGroup baseGroup = iota + 1
barGroup
)
var groups = [...]string{
fooGroup: "foo",
barGroup: "bar",
}
var xGroups = map[baseGroup]string{
fooGroup: "foo",
barGroup: "bar",
}
func main() {
fmt.Println("groups")
for k, v := range groups {
fmt.Println(k, v)
}
fmt.Println("xGroups")
for k, v := range xGroups {
fmt.Println(k, v)
}
}
If i run the code i get:
groups
0
1 foo
2 bar
xGroups
1 foo
2 bar
I'm wondering why the different outputs?
You're expecting range to behave the same on both types but in the first case it's ranging over an array and you just have an empty index 0. The value being stored in k is the current index; 0, 1, 2. In your second example you're ranging over the map and getting the key stored in k which only contains 1 and 2.
You might be wondering how is this happening? It's because this;
var groups = [...]string{
fooGroup: "foo",
barGroup: "bar",
}
Little confusing (imo very bad) code is giving you this;
var groups = [...]string{
1: "foo",
2: "bar",
}
Which is causing groups to be allocated/initialized with an empty string in index 0. Of course, the map doesn't need a key 0 to let you put something there in key 1 so it doesn't suffer from the same problems. If this is anything more than an exercise to demonstrate the features of the language I would highly recommend you get rid of the intentionally obfuscated code and use more clear constructs for constants, types and initialization.
If your skeptical about that mechanism add the following lines to your main and you can clearly see
fmt.Printf("Lenght of groups is %d and index 0 is %s\n", len(groups), groups[0])
fmt.Printf("Lenght of xGroups is %d\n", len(xGroups))

What is an idiomatic way of representing enums in Go?

I'm trying to represent a simplified chromosome, which consists of N bases, each of which can only be one of {A, C, T, G}.
I'd like to formalize the constraints with an enum, but I'm wondering what the most idiomatic way of emulating an enum is in Go.
Quoting from the language specs:Iota
Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants. It is reset to 0 whenever the reserved word const appears in the source and increments after each ConstSpec. It can be used to construct a set of related constants:
const ( // iota is reset to 0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota has been reset)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0 (untyped integer constant)
v float64 = iota * 42 // v == 42.0 (float64 constant)
w = iota * 42 // w == 84 (untyped integer constant)
)
const x = iota // x == 0 (iota has been reset)
const y = iota // y == 0 (iota has been reset)
Within an ExpressionList, the value of each iota is the same because it is only incremented after each ConstSpec:
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0
bit1, mask1 // bit1 == 2, mask1 == 1
_, _ // skips iota == 2
bit3, mask3 // bit3 == 8, mask3 == 7
)
This last example exploits the implicit repetition of the last non-empty expression list.
So your code might be like
const (
A = iota
C
T
G
)
or
type Base int
const (
A Base = iota
C
T
G
)
if you want bases to be a separate type from int.
Referring to the answer of jnml, you could prevent new instances of Base type by not exporting the Base type at all (i.e. write it lowercase). If needed, you may make an exportable interface that has a method that returns a base type. This interface could be used in functions from the outside that deal with Bases, i.e.
package a
type base int
const (
A base = iota
C
T
G
)
type Baser interface {
Base() base
}
// every base must fulfill the Baser interface
func(b base) Base() base {
return b
}
func(b base) OtherMethod() {
}
package main
import "a"
// func from the outside that handles a.base via a.Baser
// since a.base is not exported, only exported bases that are created within package a may be used, like a.A, a.C, a.T. and a.G
func HandleBasers(b a.Baser) {
base := b.Base()
base.OtherMethod()
}
// func from the outside that returns a.A or a.C, depending of condition
func AorC(condition bool) a.Baser {
if condition {
return a.A
}
return a.C
}
Inside the main package a.Baser is effectively like an enum now.
Only inside the a package you may define new instances.
You can make it so:
type MessageType int32
const (
TEXT MessageType = 0
BINARY MessageType = 1
)
With this code compiler should check type of enum
It's true that the above examples of using const and iota are the most idiomatic ways of representing primitive enums in Go. But what if you're looking for a way to create a more fully-featured enum similar to the type you'd see in another language like Java or Python?
A very simple way to create an object that starts to look and feel like a string enum in Python would be:
package main
import (
"fmt"
)
var Colors = newColorRegistry()
func newColorRegistry() *colorRegistry {
return &colorRegistry{
Red: "red",
Green: "green",
Blue: "blue",
}
}
type colorRegistry struct {
Red string
Green string
Blue string
}
func main() {
fmt.Println(Colors.Red)
}
Suppose you also wanted some utility methods, like Colors.List(), and Colors.Parse("red"). And your colors were more complex and needed to be a struct. Then you might do something a bit like this:
package main
import (
"errors"
"fmt"
)
var Colors = newColorRegistry()
type Color struct {
StringRepresentation string
Hex string
}
func (c *Color) String() string {
return c.StringRepresentation
}
func newColorRegistry() *colorRegistry {
red := &Color{"red", "F00"}
green := &Color{"green", "0F0"}
blue := &Color{"blue", "00F"}
return &colorRegistry{
Red: red,
Green: green,
Blue: blue,
colors: []*Color{red, green, blue},
}
}
type colorRegistry struct {
Red *Color
Green *Color
Blue *Color
colors []*Color
}
func (c *colorRegistry) List() []*Color {
return c.colors
}
func (c *colorRegistry) Parse(s string) (*Color, error) {
for _, color := range c.List() {
if color.String() == s {
return color, nil
}
}
return nil, errors.New("couldn't find it")
}
func main() {
fmt.Printf("%s\n", Colors.List())
}
At that point, sure it works, but you might not like how you have to repetitively define colors. If at this point you'd like to eliminate that, you could use tags on your struct and do some fancy reflecting to set it up, but hopefully this is enough to cover most people.
There is a way with struct namespace.
The benefit is all enum variables are under a specific namespace to avoid pollution.
The issue is that we could only use var not const
type OrderStatusType string
var OrderStatus = struct {
APPROVED OrderStatusType
APPROVAL_PENDING OrderStatusType
REJECTED OrderStatusType
REVISION_PENDING OrderStatusType
}{
APPROVED: "approved",
APPROVAL_PENDING: "approval pending",
REJECTED: "rejected",
REVISION_PENDING: "revision pending",
}
As of Go 1.4, the go generate tool has been introduced together with the stringer command that makes your enum easily debuggable and printable.
I am sure we have a lot of good answers here. But, I just thought of adding the way I have used enumerated types
package main
import "fmt"
type Enum interface {
name() string
ordinal() int
values() *[]string
}
type GenderType uint
const (
MALE = iota
FEMALE
)
var genderTypeStrings = []string{
"MALE",
"FEMALE",
}
func (gt GenderType) name() string {
return genderTypeStrings[gt]
}
func (gt GenderType) ordinal() int {
return int(gt)
}
func (gt GenderType) values() *[]string {
return &genderTypeStrings
}
func main() {
var ds GenderType = MALE
fmt.Printf("The Gender is %s\n", ds.name())
}
This is by far one of the idiomatic ways we could create Enumerated types and use in Go.
Edit:
Adding another way of using constants to enumerate
package main
import (
"fmt"
)
const (
// UNSPECIFIED logs nothing
UNSPECIFIED Level = iota // 0 :
// TRACE logs everything
TRACE // 1
// INFO logs Info, Warnings and Errors
INFO // 2
// WARNING logs Warning and Errors
WARNING // 3
// ERROR just logs Errors
ERROR // 4
)
// Level holds the log level.
type Level int
func SetLogLevel(level Level) {
switch level {
case TRACE:
fmt.Println("trace")
return
case INFO:
fmt.Println("info")
return
case WARNING:
fmt.Println("warning")
return
case ERROR:
fmt.Println("error")
return
default:
fmt.Println("default")
return
}
}
func main() {
SetLogLevel(INFO)
}
For a use case like this, it may be useful to use a string constant so it can be marshaled into a JSON string. In the following example, []Base{A,C,G,T} would get marshaled to ["adenine","cytosine","guanine","thymine"].
type Base string
const (
A Base = "adenine"
C = "cytosine"
G = "guanine"
T = "thymine"
)
When using iota, the values get marshaled into integers. In the following example, []Base{A,C,G,T} would get marshaled to [0,1,2,3].
type Base int
const (
A Base = iota
C
G
T
)
Here's an example comparing both approaches:
https://play.golang.org/p/VvkcWvv-Tvj
Here is an example that will prove useful when there are many enumerations. It uses structures in Golang, and draws upon Object Oriented Principles to tie them all together in a neat little bundle. None of the underlying code will change when a new enumeration is added or deleted. The process is:
Define an enumeration structure for enumeration items: EnumItem. It has an integer and string type.
Define the enumeration as a list of enumeration items: Enum
Build methods for the enumeration. A few have been included:
enum.Name(index int): returns the name for the given index.
enum.Index(name string): returns the name for the given index.
enum.Last(): returns the index and name of the last enumeration
Add your enumeration definitions.
Here is some code:
type EnumItem struct {
index int
name string
}
type Enum struct {
items []EnumItem
}
func (enum Enum) Name(findIndex int) string {
for _, item := range enum.items {
if item.index == findIndex {
return item.name
}
}
return "ID not found"
}
func (enum Enum) Index(findName string) int {
for idx, item := range enum.items {
if findName == item.name {
return idx
}
}
return -1
}
func (enum Enum) Last() (int, string) {
n := len(enum.items)
return n - 1, enum.items[n-1].name
}
var AgentTypes = Enum{[]EnumItem{{0, "StaffMember"}, {1, "Organization"}, {1, "Automated"}}}
var AccountTypes = Enum{[]EnumItem{{0, "Basic"}, {1, "Advanced"}}}
var FlagTypes = Enum{[]EnumItem{{0, "Custom"}, {1, "System"}}}
Refactored https://stackoverflow.com/a/17989915/863651 to make it a bit more readable:
package SampleEnum
type EFoo int
const (
A EFoo = iota
C
T
G
)
type IEFoo interface {
Get() EFoo
}
func(e EFoo) Get() EFoo { // every EFoo must fulfill the IEFoo interface
return e
}
func(e EFoo) otherMethod() { // "private"
//some logic
}
This is a safe way to implement enum in golang:
package main
import (
"fmt"
)
const (
MALE = _gender(1)
FEMALE = _gender(2)
RED = _color("RED")
GREEN = _color("GREEN")
BLUE = _color("BLUE")
)
type Gender interface {
_isGender()
Value() int
}
type _gender int
func (_gender) _isGender() {}
func (_g _gender) Value() int {
return int(_g)
}
type Color interface {
_isColor()
Value() string
}
type _color string
func (_color) _isColor() {}
func (_c _color) Value() string {
return string(_c)
}
func main() {
genders := []Gender{MALE, FEMALE}
colors := []Color{RED, GREEN, BLUE}
fmt.Println("Colors =", colors)
fmt.Println("Genders =", genders)
}
The output:
Colors = [RED GREEN BLUE]
Genders = [1 2]
Also, this is a pretty effective way to store different roles in one location in a byte, where the first value is set to 1, bit shifted by an iota.
package main
import "fmt"
const (
isCaptain = 1 << iota
isTrooper
isMedic
canFlyMars
canFlyJupiter
canFlyMoon
)
func main() {
var roles byte = isCaptain | isMedic | canFlyJupiter
//Prints a binary representation.
fmt.Printf("%b\n", roles)
fmt.Printf("%b\n", isCaptain)
fmt.Printf("%b\n", isTrooper)
fmt.Printf("%b\n", isMedic)
fmt.Printf("Is Captain? %v\n", isCaptain&roles == isCaptain)
fmt.Printf("Is Trooper? %v", isTrooper&roles == isTrooper)
}
I created the enum this way. Suppose we need an enum representing gender. Possible values are Male, Female, Others
package gender
import (
"fmt"
"strings"
)
type Gender struct {
g string
}
var (
Unknown = Gender{}
Male = Gender{g: "male"}
Female = Gender{g: "female"}
Other = Gender{g: "other"}
)
var genders = []Gender{
Unknown,
Male,
Female,
Other,
}
func Parse(code string) (parsed Gender, err error) {
for _, g := range genders {
if g.g == strings.ToLower(code) {
if g == Unknown {
err = fmt.Errorf("unknown gender")
}
parsed = g
return
}
}
parsed = Unknown
err = fmt.Errorf("unknown gender", code)
return
}
func (g Gender) Gender() string {
return g.g
}
A simpler way I have found to work.
const (
Stake TX = iota
Withdraw)
type TX int
func (t TX) String() string {
return [...]string{"STAKE", "WITHDRAW"}[t]}
log.Println(Stake.String()) --> STAKE

Resources