16

Reading about Delphi's Exit statement (see here for instance), I cannot ignore that writing about it every author feels the duty to give a piece of advice, for example:

Warning : use with caution - jumping is a concept at odds with structured coding - it makes code maintenance difficult.

Now, I'm coming from C and C++ in Unix and I'm familiar with re-entrancy issues, but honestly I cannot figure out why in Delphi returning from a function before it reaches its natural end should be evil.

Unless every function and procedure in Delphi is considered as re-entrant.

What am I missing?

Mansfield
  • 14,445
  • 18
  • 76
  • 112
Federico Zancan
  • 4,846
  • 4
  • 44
  • 60
  • This question isn't really appropriate here, as it asks for discussion and speculation. It really belongs on [programmers](http://programmers.stackexchange.com) instead where it's more appropriate. The [FAQ](http://stackoverflow.com/faq) specifically mentions this type of question as not being a good fit with the format and design of this site. Voting to migrate to programmers. – Ken White Mar 23 '12 at 16:49
  • I suggest you the following article http://programmers.stackexchange.com/questions/77530/best-practices-concerning-exit-in-delphi It covers the same subject in a detailed manner. – RBA Mar 23 '12 at 16:36
  • 1
    Linked site is well known for misunderstanding and poor quality information. Ignore it. What he unsuccessfully tries to say is *single exit point* concept. – OnTheFly Mar 23 '12 at 17:11
  • We can migrate it to programmers where it should be closed as a duplicate of this: http://programmers.stackexchange.com/questions/77530/best-practices-concerning-exit-in-delphi – David Heffernan Mar 23 '12 at 17:32
  • @DavidHeffernan: the discussion about the guard clause and whether to use exit or not is interesting but imho doesn't give any explicit reason about if and why Exit should be used with extra caution as suggested, if we don't consider readability aspects, which sure are crucial for humans. Btw, I perceived the advices of caution I encountered in the articles I read as to be related to code efficiency or issues. Learning C I can't remember any dramatic advice for "return". My question was indeed related to this. What can I do to normalize this question and make it good for other users to read? – Federico Zancan Mar 26 '12 at 10:24
  • 1
    It's all about readability. There are no issues of efficiency. The site you linked to, Delphi Basics, is very poor, IMHO. Don't use it to guide you. But in this case, it's quite clear. It gives the advice regarding exit on grounds of code maintainability, just as all your answers do. That's the only issue here. – David Heffernan Mar 26 '12 at 10:30
  • @DavidHeffernan: Objective reached, thank you. – Federico Zancan Mar 26 '12 at 13:49
  • "*jumping is a concept at odds with structured coding*" - So are/do exceptions. Even messier, because `Exit` only leaves the function, while exceptions may bubble up several levels until the next handler catches them. Technically, they're just an high-level goto. Yet everybody uses them, because they have clear advantages. – JensG May 30 '15 at 12:37

4 Answers4

12

There are two schools of thought.

One school of thought says that functions should have a single exit point. Many many coding standards enforce that as a rule. The motivation for this comes from the hard experience of maintaining spaghetti code full of large functions with gotos, multiple exits and so on.

The other school of thought says that spaghetti code is bad but one should be pragmatic and judge coding styles on their merits rather than following dogmatic rules. For example, many programmers feel that guard clauses are much preferable to the deeply indented functions that arise when you refrain from using exit. As an illustration consider the following example from Martin Fowler's excellent refactoring catalog: Replace Nested Conditional with Guard Clauses.

Fundamentally it all comes down to personal preference. I personally encourage the use of guard clauses, but refrain from wild use of exit in long procedures.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 3
    +1. Even though I think you are stating the obvious, you phrased it pretty well! – Andreas Rejbrand Mar 23 '12 at 16:34
  • 1
    If used correctly there is nothing evil in Exit. I use it when appropriate. Like with every other tool its just that a tool. I prefer a clear exit at the begining of the procedure while checking some condition over IF-ELSE for example – Runner Mar 23 '12 at 17:55
9

You're missing that it doesn't say "don't use this" or "Exit is evil"; it says "use it carefully." And it says that it can make maintenance difficult. For example, if you've got a large method and there's an line like this somewhere in the middle, you might miss it entirely:

if aLocalObject.CheckSomeValue(aParameter) <> RIGHT_VALUE then Exit;

And yeah, I actually have seen stuff like that before, unfortunately. :(

A lot of the problems can be mitigated with a few rules of thumb:

  • Always put Exit (and Break and Continue) statements on their own line. It makes them harder to miss.
  • Favor small methods over larger ones, and break down large methods into smaller ones when reasonable. (WHEN REASONABLE! This is not an absolute rule, and applying it too zealously can make things worse instead of better. Keep Einstein's quote in mind: "Make everything as simple as possible, but not simpler.")
  • Learn to use try/finally blocks so that Exiting out of a procedure is safe to do without causing leaks or corruption.
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • 1
    OK, so *that's* why [I didn't get any upvotes!](http://stackoverflow.com/questions/9841515/list-all-web-browsers-installed-on-a-machine) :) – Andreas Rejbrand Mar 23 '12 at 16:36
2

In addition to David's and Mason's superb answers, I would like to share my personal Exit usage favorite. Let's call it, quite contrary to "safe guard", a "last resort provider". ;)

The basic idea:

function Search(List: TList; Item: TObject): Integer;
begin
  for Result := 0 to List.Count - 1 do
    if List[Result] = Item then
      Exit;
  Result := -1;
end;

Other more realistic examples (from this answer and this answer):

const 
  Order: array[0..6] of String = ('B', 'C', 'A', 'D', 'G', 'F', 'E'); 

function GetStringOrder(const S: String; CaseSensitive: Boolean): Integer; 
begin 
  for Result := 0 to Length(Order) - 1 do 
    if (CaseSensitive and (CompareStr(Order[Result], S) = 0)) or 
        (not CaseSensitive and (CompareText(Order[Result], S) = 0)) then 
      Exit; 
  Result := Length(Order); 
end; 

function FindControlAtPos(Window: TWinControl; const ScreenPos: TPoint): TControl; 
var 
  I: Integer; 
  C: TControl; 
begin 
  for I := Window.ControlCount - 1 downto 0 do 
  begin 
    C := Window.Controls[I]; 
    if C.Visible and PtInRect(C.ClientRect, C.ScreenToClient(ScreenPos)) then 
    begin 
      if C is TWinControl then 
        Result := FindControlAtPos(TWinControl(C), ScreenPos) 
      else 
        Result := C; 
      Exit; 
    end; 
  end; 
  Result := Window; 
end; 

And concluding with a quote from Delphi's help on the compiler error message FOR-Loop variable '<element>' may be undefined after loop:

You can only rely on the final value of a for loop control variable if the loop is left with a goto or exit statement.

Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
0

Like many things, exit is useful in some cases and not in others. I find it invaluable in those little functions that search through some container, loading an object from the container into 'result' inside the loop and looking for a matching property. If found, the function can just exit, if not, the result can be set to nil after the loop, just before the 'normal' return.

Also, surely there can be few developers who, while debugging, have not shoved in an 'exit' at the top of a procedure to stop it executing...

I can only agree with the points raised by the other posters re. guard clauses, exits buried in large, complex procedures etc.

Martin James
  • 24,453
  • 3
  • 36
  • 60