The downside, of course, is that when you want to print those values:type DaysOfTheWeek int const ( Sunday DaysOfTheWeek = iota Monday Tuesday Wednesday Thursday Friday )
While the "stringer" code-generator is the recommend way of handling this, I'm often tempted to simply change the declaration:fmt.Println(Sunday) // prints 0, would rather it print "Sunday"
But, this is wrong in an important way.type DaysOfTheWeek string const ( Sunday DaysOfTheWeek = "Sunday" Monday = "Monday" Tuesday = "Tuesday" Wednesday = "Wednesday" Thursday = "Thursday" Friday = "Friday" )
Assertions, and reflection will fail.
We expect that both statements return true, but in fact the second statement is false. If you are using testify, an assertion on the value will also fail.import "reflect" func ExampleDay() DaysOfTheWeek { return Friday } func main() { fmt.Println(reflect.DeepEqual(Friday, Friday)) // true fmt.Println(reflect.DeepEqual(Friday, ExampleDay())) // false! }
The reason is that DeepEqual requires types match, and that the definition of const lists, in effect, resets the type declaration for each line containing an equals sign.func TestSomething(t *testing.T) { // passes, these are the same type and same object. assert.Equal(t, Friday, Friday) // fails, these are of different types! assert.Equal(t, Friday, ExampleDay()) }
In the first const example, using iota, there's only one equal sign: so all of the constants get the same type.
In the second const example, even though we started the list with Sunday as a type of "DaysOfTheWeek", the presence of the equal sign on the line which declares Monday resets the type declaration engine to its defaults. In this case, a string. So, only Sunday is of our enumerated type. Monday-Saturday are all of type "string".
If we redeclare our const list:
Then reflect, and asserts, will work as expected:const ( Sunday DaysOfTheWeek = "Sunday" Monday DaysOfTheWeek = "Monday" Tuesday DaysOfTheWeek = "Tuesday" Wednesday DaysOfTheWeek = "Wednesday" Thursday DaysOfTheWeek = "Thursday" Friday DaysOfTheWeek = "Friday" )
Unfortunately, unlike with iota, there's no pretty way to declare all of those strings without repeating the type. The closest we can get looks like this:func main() { fmt.Println(reflect.DeepEqual(Friday, ExampleDay()) // true! } func TestSomething(t *testing.T) { // passes, these are of now the same type! assert.Equal(t, Friday, ExampleDay()) }
Which is error prone, and looks a little gross with the default go formatter, but is at least easy to generate, and more importantly: it's correct.const ( ParamTypeUnknown, ParamTypeCommand, ParamTypeArray, ParamTypePrim, ParamTypeBlob ParamType = "ParamTypeUnknown", "ParamTypeCommand", "ParamTypeArray", "ParamTypePrim", "ParamTypeBlob" )
See full post...