Question

There are many discussions related to whether it is better to have only one or multiple exit points per function, but it is not clear to me which statements can be considered as exit points: only return or some other ones as well?

For example, should we consider throw and break as exit points?

Are there 2 or 3 exit points?

func(x, y) {
  // guard
  if (!x)
    throw "You have an error here!"

  if (y)
    return "foo"
  else
    return "bar"
}

Are there 1 or zero exit points?

func() {
  i = 1
  loop {
    if (i == 5)
      break
    
    show_message(i)
    i = i + 1
  }
}
Was it helpful?

Solution

For a function, the exit point are all the places where the function is left:

  • return statement
  • reaching the end of the function body, if the language allows it (implicit return)
  • terminating the program, if the language allows this (e.g.exit()).
  • explicit throwing: it's exceptional, but de facto you leave the function.
  • implicit throwing: let's be clear, very often code that is not exception safe might exit a function and might leave some mess, especially if the function allocated some resources from the OS (e.g. OS mutex set, OS temporary file created but not destroyed, etc...)

But all those real exit points are not relevant, when it comes to discussing single exit point. Such discussions are about code readability, and are only about intentional exits according to the normal flow of operations. Only returns and terminations are relevant in this regard.

The exception handling follows an alternate flow excluded from the single exit point philosphy. In fact, exception handling can make exceptional cases safer and more readable, than the equivalent code without exception handling (see in this regard, the section "exception" of MISRA C++ 6-6-5,and Autosar C++14 section 6.8.4, which are both secure coding standards and exclude exceptions from the single exit point philosophy that both promote).

Licensed under: CC-BY-SA with attribution
scroll top