This bug was frustrating enough that half-way through squashing it, I promised myself I would document the solution. Unfortunately, it’s not a particularly interesting bug, but a promise is a promise!
I had some code very much like:
typedef enum {NoError, ReadError, WriteError, NetworkError, UnknownError} ErrorType;
...
void DescriptionOfError(ErrorType err, char *string)
{
switch(err)
{
IOError:
strcpy(string, "Could not read data.");
break;
WriteError:
strcpy(string, "Could not write data!");
break;
NetworkError:
strcpy(string, "Network Error. Make sure you have an internet connection and try again");
break;
NoError:
strcpy(string, "No error.");
break;
default:
UnknownError:
strcpy(string, "Unknown error.");
break;
}
}
int main (int argc, char** argv)
{
char desc[1024];
DescriptionOfError(ReadError,desc);
printf("error: %s\n", desc);
return 0;
}
And it just wouldn’t do the right thing, but for the life of me I couldn’t see what was wrong.
The “solution” was very simple, and somewhat embarrassing. I forgot the case
keyword before the labels in the switch
statement. Turns out that if you don’t have a case
in-front of a label, it’s treated like a goto
label, not a switch
label. And this is something that I’ve known for years, but for 20 minutes I kept reading the code, and my brain would interpret what it should say, not literally analyze it.
This was extremely frustrating, not because it took me a long time to fix (I wish all my bugs could be squashed in just 20 minutes!), but because the results I was seeing totally violated my mental model of what should have happened. Violating someone’s mental model is more unsettling then you might imagine — avoid doing it at all costs.