It's the people who oppose break, continue AND goto who are insane. (And I believe it's largely the same crowd who advocate only one return per function or method, too.)
While I do agree that for educational purposes, it's good that they are avoided for a good while UNTIL the student thoroughly understands why they needed to avoid them. But as things stand, I do feel that most people take the principle too far, and in fact I believe the need for try-catch(-finally) largely comes from trying to avoid goto at all costs.
There is no try-catch in C, so we end up seeing a lot of code like this:
ret=SUCCESS;
if(init_stuff()) {
if(a=get_a(...)) {
if(b=get_b(...)) {
if(c=get_c(...)) {
if(!do(a,b,c)) {
ret=FAILED;
log("error doing a,b,c");
}
} else {
ret=FAILED;
log("error getting c");
}
cleanup(c);
} else {
ret=FAILED;
log("error getting b");
}
cleanup(b);
} else {
ret=FAILED;
log("error getting a");
}
cleanup(a);
cleanup_stuff();
} else
ret=FAILED;
return ret;
And not only do people write functions like that, they'll actually go to great lengths to defend that it is the appropriate way to do it!
If I were to rewrite the above like this:
error=NO_ERROR;
if(!init_stuff())
return FAILED;
if(!(a=get_a(...))) { error=ERROR_A; goto cleanup_a; }
if(!(b=get_b(...))) { error=ERROR_B; goto cleanup_b; }
if(!(c=get_c(...))) { error=ERROR_C; goto cleanup_c; }
if(!do(a,b,c)) { error=ERROR_DO_ABC; }
cleanup_c: cleanup(c);
cleanup_b: cleanup(b);
cleanup_a: cleanup(a);
cleanup_stuff();
if(error!=NO_ERROR) {
log(error_msgs[error]);
return FAILED;
}
return SUCCESS;
A lot of people would protest the "bad practices" I'm using (although it works almost exactly like try-catch). I'd also probably be let known how readability suffers because the code has not been properly indented.