exhaustive icon indicating copy to clipboard operation
exhaustive copied to clipboard

Check enum based on struct

Open maratori opened this issue 2 years ago • 4 comments

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
}

maratori avatar Dec 14 '21 14:12 maratori

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.

nishanths avatar Jan 26 '22 10:01 nishanths

+1 to this. struct based enums are more robust in Go. would like to see this project to support it.

nikolaydubina avatar Nov 15 '22 13:11 nikolaydubina

I would rather wrap struct enums over integer type. So it is very similar to current version.

nikolaydubina avatar Nov 15 '22 13:11 nikolaydubina

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.

navijation avatar Mar 02 '23 15:03 navijation