Open
Description
I'd like to propose that enum be added to Go as a special kind of type
. The examples below are borrowed from the protobuf example.
Enums in Go today
type SearchRequest int
var (
SearchRequestUNIVERSAL SearchRequest = 0 // UNIVERSAL
SearchRequestWEB SearchRequest = 1 // WEB
SearchRequestIMAGES SearchRequest = 2 // IMAGES
SearchRequestLOCAL SearchRequest = 3 // LOCAL
SearchRequestNEWS SearchRequest = 4 // NEWS
SearchRequestPRODUCTS SearchRequest = 5 // PRODUCTS
SearchRequestVIDEO SearchRequest = 6 // VIDEO
)
type SearchRequest string
var (
SearchRequestUNIVERSAL SearchRequest = "UNIVERSAL"
SearchRequestWEB SearchRequest = "WEB"
SearchRequestIMAGES SearchRequest = "IMAGES"
SearchRequestLOCAL SearchRequest = "LOCAL"
SearchRequestNEWS SearchRequest = "NEWS"
SearchRequestPRODUCTS SearchRequest = "PRODUCTS"
SearchRequestVIDEO SearchRequest = "VIDEO"
)
// IsValid has to be called everywhere input happens, or you risk bad data - no guarantees
func (sr SearchRequest) IsValid() bool {
switch sr {
case SearchRequestUNIVERSAL, SearchRequestWEB...:
return true
}
return false
}
How it might look with language support
enum SearchRequest int {
0 // UNIVERSAL
1 // WEB
2 // IMAGES
3 // LOCAL
4 // NEWS
5 // PRODUCTS
6 // VIDEO
}
enum SearchRequest string {
"UNIVERSAL"
"WEB"
"IMAGES"
"LOCAL"
"NEWS"
"PRODUCTS"
"VIDEO"
}
The pattern is common enough that I think it warrants special casing, and I believe that it makes code more readable. At the implementation layer, I would imagine that the majority of cases can be checked at compile time, some of which already happen today, while others are near impossible or require significant tradeoffs.
- Safety for exported types: nothing prevents someone from doing
SearchRequest(99)
orSearchRequest("MOBILEAPP")
. Current workarounds include making an unexported type with options, but that often makes the resulting code harder to use / document. - Runtime safety: Just like protobuf is going to check for validity while unmarshaling, this provides language wide validation, anytime that an enum is instantiated.
- Tooling / Documentation: many packages today put valid options into field comments, but not everyone does it and there is no guarantee that the comments aren't outdated.
Things to Consider
- Nil: by implementing
enum
on top of the type system, I don't believe this should require special casing. If someone wantsnil
to be valid, then the enum should be defined as a pointer. - Default value / runtime assignments: This is one of the tougher decisions to make. What if the Go default value isn't defined as a valid enum? Static analysis can mitigate some of this at compile time, but there would need to be a way to handle outside input.
I don't have any strong opinions on the syntax. I do believe this could be done well and would make a positive impact on the ecosystem.