exhaustive
exhaustive copied to clipboard
Check enum based on struct
There is alternative approach how to deal with enums in Go. Example: https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/blob/master/internal/trainer/domain/hour/availability.go Article with motivation: https://threedots.tech/post/safer-enums-in-go/.
The idea is to use struct with private field and package level variables. It would be great if exhaustive checks such enums as well, not only constants.
var (
Available = Availability{"available"}
NotAvailable = Availability{"not_available"}
TrainingScheduled = Availability{"training_scheduled"}
)
var availabilityValues = []Availability{
Available,
NotAvailable,
TrainingScheduled,
}
// Availability is enum.
//
// Using struct instead of `type Availability string` for enums allows us to ensure,
// that we have full control of what values are possible.
// With `type Availability string` you are able to create `Availability("i_can_put_anything_here")`
type Availability struct {
a string
}
func NewAvailabilityFromString(availabilityStr string) (Availability, error) {
for _, availability := range availabilityValues {
if availability.String() == availabilityStr {
return availability, nil
}
}
return Availability{}, errors.Errorf("unknown '%s' availability", availabilityStr)
}
// Every type in Go have zero value. In that case it's `Availability{}`.
// It's always a good idea to check if provided value is not zero!
func (h Availability) IsZero() bool {
return h == Availability{}
}
func (h Availability) String() string {
return h.a
}
This would be useful tooling generally. I'll have to think about whether I want to make it part of this program though. Primarily I want to think through the patterns/conventions that mark a given struct as an enum struct.
This might be interesting: https://github.com/BurntSushi/go-sumtype.
+1 to this. struct based enums are more robust in Go. would like to see this project to support it.
I would rather wrap struct enums over integer type. So it is very similar to current version.
I think it would be good to be able to specify that any type could be an enum with a //exhaustive:enumerated
tag, which should forbid any "instantiations" of the type outside the package's var block, + an equivalent regex configuration option for 3rd party types. What qualifies as an "instantiation" is a bit vague though if you want to deal with edge cases like pointer assignment.
Although perhaps that should be a different analyzer entirely or gated by a flag to avoid the extra cost for callers that don't want this feature.