I hate nesting (not the bird kind)

I’ve been re-reading a few of my favourite programming books over the last couple of months, and I just have to mention this part of The Practice of Programming, by Brian Kernighan and Rob Pike. Not because it’s particularly earth-shattering, but simply because it’s the best explanation I’ve read of how to layout if/else control structures properly.

Sound trivial? It should be, but poor control structure layout is one of my pet hates. I see this sort of thing so often in real code and it grates every time:

if(argc == 3)
if((fin = fopen(argv[2], “r”)) != NULL)
if((fout = fopen(argv[2], “w”)) != NULL) {
while((c = getc(fin)) != EOF)
putc(c, fout);
fclose(fin); fclose(fout);
}
else
printf(”Can’t open output file %s\n, argv[2]);
else
printf(”Can’t open input file %s\n”, argv[1]);
else
printf(”Usage: cp inputfile outputfile\n”);

Actually, I’m being too generous. Imagine the same control structure spread over 200 lines and your’re getting close to the monsters I’ve slain in the past. Kernighan and Pike have this to say about the code:

The sequence of ifs requires us to maintain a mental pushdown stack of what tests were made, so that at the appropriate point we can pop them until we determine the corresponding action (if we can still remember) .

The authors suggest that the correct way to lay out the code for maximum readability is instead:

if(argc != 3)
printf(”Usage: cp inputfile outputfile\n”);
else if ((fin = fopen(argv[1], “r”)) == NULL)
printf(”Can’t open input file %s\n”, argv[1]);
else if ((fout = fopen(argv[2], “w”)) == NULL) {
printf(”Can’t open output file %s\n”, argv[2]);
fclose(fin);
}
else {
while((c = getc(fin) != EOF)
putc (c, fout);
fclose(fin);
fclose(fout);
}

Note how inverting the boolean tests (== becomes !=) allows the code to be greatly simplified. I find that if this technique is used in conjunction with short, well contained functions, it’s often not necessary to use else at all. Instead, if a test fails, the function returns immediately.

Kernighan and Pike provide a well-worded summary of the principle:

The rule is to follow each decision as closely as possible by its associated action. Or, to put it another way, each time you make a test, do something.