Some things for enumerated constants in Go
The standard way to do C style enum
s in Go
is with iota
and a typed set
of constants. This looks like this:
type SmtpCmd intconst ( EHLO SmtpCmd = iota MAILFROM RCPTTO .... )
This set of constants is perfectly fine in one sense, but it has a
non-obvious drawback (at least for someone coming from a Python
world): you can't tell an uninitialized instance of SmtpCmd
from
an EHLO
value. Like all Go types, SmtpCmd
has a zero value and
since it's ultimately an integer the zero value is, well, 0. iota
starts numbering from 0 so EHLO
is also 0. Having your zero value
overlap with a valid value that you can see normally has the
possibility of hiding errors like uninitialized fields or unset
return values. Instead of being glaringly obvious it will just look
like your code is somehow incorrectly determining that you have an
EHLO
.
The simple fix for this is to introduce a dummy first value:
const ( unsetCmd SmtpCmd = iota EHLO .... )
(We make unsetCmd
start with a lower case letter so that it won't
be visible from outside the package. This may or may not be a good
idea depending on whether outside people generate instances of
SmtpCmd
or just look at them.)
The other thing you can do in Go with a typed set of constants is
to pretty-print them for the purposes of the %v
formatting operator.
All this takes is a String()
method function defined for your
type. Doing a minimal version that annotates the string value with
type information is simple but perhaps not completely useful:
func (v SmtpCmd) String() string { return fmt.Sprintf("<SmtpCmd %d>", v) }
More sophisticated versions are possible if you have either a source
of number to name mapping information or just build one yourself.
You probably don't need it to be strikingly efficient because you
probably won't be using %v
on your enumerated types very often.
(One thing you can do in even a simple version is specifically
annotate unsetCmd
and perhaps anything that's out of range for
your actual defined constants.)
Sidebar: A potential trick with String()
methods
In many cases being able to use %v
on your package's values is
most useful when debugging test failures. In addition, printing
useful information about them may involve crusty code that you
don't really like sitting around in the main package source.
As far as I can tell, you can actually put the String()
method
definitions in your *_test.go
files and have them work. The
*_test.go
packages are an internal part of your package when
built for tests, but I believe that stuff defined in them is not
part of your package's exported functionality and the crufty and
internals specific pretty-printing code can live with the rest of
your testing code.
(Of course this may cause a certain amount of confusion someday,
when your package internal tests print out nice looking and informative
%v
values for things while in your outside code they just gets
eg basic numbers. This is your call to make. I honestly don't know
which side I come down on right now, although I get a half-pass
because I'm not writing a package I expect anyone else to ever use.)
Comments on this page:
|
|