Go doesn't (yet) have enums as such, but it seems common to use constants
with iota
for this instead.
Passing enums instead of strings to a constructor helps reduce possible bugs; when a constructor accepts a set of possible options for an argument, it's easy to "fat finger" (i.e. misspell) them.
Using custom "enums" helps to prevent this and also documents available options for that argument.
Here's what I found on stackoverflow on the why (above), and here's an example I've implemented.
https://github.com/alex-leonhardt/go-passing-enums/
main.go
package main
import (
"fmt"
"github.com/alex-leonhardt/go-passing-enums/pkg/config"
)
func main() {
cfg, err := config.New(config.Env)
fmt.Printf("%#v %#v\n", cfg, err)
cfg, err = config.New(config.File)
fmt.Printf("%#v %#v\n", cfg, err)
cfg, err = config.New(42)
fmt.Printf("%#v %#v\n", cfg, err)
}
we're using the New() constructor function in the
config
package and pass it the config type to return
pkg/config/config.go
package config
import (
"errors"
"github.com/alex-leonhardt/go-passing-enums/pkg/config/env"
"github.com/alex-leonhardt/go-passing-enums/pkg/config/file"
)
// Configurer describes a config provider
type Configurer interface {
Get(string) string
Set(string) bool
Del(string) bool
}
// T is used to select the type of config to return
type T uint
// Constants for T
const (
Env T = iota
File
Unknown
)
// New takes a config.T type and returns an Configurer
func New(t T) (Configurer, error) {
switch t {
case Env:
return env.New(), nil
case File:
return file.New(), nil
default:
return nil, errors.New("eh?")
}
}
we're specifying
T
as auint
which we'll use as our custom "enum" to pass toNew()
, depending on the "type", a differentConfigurer
is being initialised (using its ownNew()
constructor) and then returned
pkg/config/env/env.go
package env
type env struct {
val1 string
val2 int
val3 bool
}
// New creates a new config
func New() *env {
return &env{}
}
func (c *env) Get(v string) string {
return ""
}
func (c *env) Set(v string) bool {
return true
}
func (c *env) Del(v string) bool {
return true
}
finally,
New()
returns a pointer to an initialisedenv
struct which satisfies theconfig.Configurer
interface by implementing the required methodsGet
,Set
andDel
.
Hope this helps anyone, it helped me already just by writing this!
If you have any comments, suggestions, better explanations, please do leave a comment. Always happy to learn new things!
Top comments (2)
Guys from Uber recommends to start enum from 1, zero is default(not initialized) value, in this case, you no need "unknown" value
github.com/uber-go/guide/blob/mast...
Thanks! That's actually a good point, although there are valid cases for starting at
0
.Thinking about it, defaulting to
0
in the example case above is probably actually a valid thing to do.Nevertheless, thanks for the link and tip! I'll have a read through what Uber does and try to learn from them too.