I’m not going to address goto vs defer and just talk about the control flow graph structure:
Note that goto for error handling (and similar uses) maps directly to structured programming with multi-level exits. Translating the article’s example to Rust using this recipe:
'err_mem: {
// open the fd
if fd == -1 { break 'err_mem; }
// return successfully
}
// free mem
// return unsuccessfully
It maps to Wasm’s structured control flow in exactly the same way: br or br_if to an enclosing block.
Anyway, this should convince you that the difference is not that the control flow is unstructured (irreducible) with goto error handling and structured (reducible) with multi-level exits. The difference is that with blocks and multi-level exits the syntactic nesting (and indentation) grows as you add multiple exit points. But you can change that by making different syntax design choices in your language.
And if you continue to use goto for C error handling (like many of us do), then you can use this to help win the argument the next time someone objects. :)
I’m not going to address goto vs defer and just talk about the control flow graph structure:
Note that goto for error handling (and similar uses) maps directly to structured programming with multi-level exits. Translating the article’s example to Rust using this recipe:
It maps to Wasm’s structured control flow in exactly the same way: br or br_if to an enclosing block.
Anyway, this should convince you that the difference is not that the control flow is unstructured (irreducible) with goto error handling and structured (reducible) with multi-level exits. The difference is that with blocks and multi-level exits the syntactic nesting (and indentation) grows as you add multiple exit points. But you can change that by making different syntax design choices in your language.
And if you continue to use goto for C error handling (like many of us do), then you can use this to help win the argument the next time someone objects. :)
score 5 · canonical