There are three so-called structural jump operators that are sort of like goto but much nicer: return, break and continue. By default, their behavior is as follows:
- return returns from the nearest enclosing function, and not a function literal
- break terminates the nearest enclosing loop
- continue either proceeds to the next step of the nearest enclosing loop or to the next branch in the nearest enclosing when expression
Break and continue at labels
Any expression in Kotlin may be marked with a label. Labels have the from of the
@ sign followed by an optional identifier, for examples
@fooBar are valid labels (see the grammar). To label an expression, we just put a label in front of it:
Now, we can qualify a break or continue with a label:
A break qualified with a label jumps to the execution point right after the loop marked with that label. A continue proceeds to the next iteration of that loop.
Return at labels
With function literals, local functions and object expression, functions can be nested in Kotlin. Qualified return's allow us to return from an outer function. The most important use case is returning from a function literal. Recall that when we write this:
The return expression returns from the nearest enclosing function, i.e.
foo. If we need to return from a function literal, we have to label it and qualify the return:
Now, it returns only from the function literal. Often times it is more convenient to use the shortest implicit label
@ for function literals:
Note that such non-local returns are supported only for function literals passed to inline-functions.
When returning a value, the parser gives preference to the qualified return, i.e.
means "return 1 at label @a" and not "return a labeled expression (@a 1)".
Named functions automatically define labels:
Break and continue in custom control structures
Inline functions make writing performant "custom control structures" easy, for example, the
forEach() function that executes a function literal for every element in a collection:
Note that this function is not exactly a redundant example easily substitutable by a normal for loop. Consider the following code:
Now, what happens when we write break (or continue) inside the body of
forEach? We simply get a compile-time error, because, lexically, there's no loop to break:
But, actually, there is a loop, hidden inside
forEach, and it is inlined there, so we should be able to tell the compiler to understand that. An we can, by annotating the loop inside
forEach with the
loop annotation. The function parameter should also be annotated with
Now, the compiler allows break and continue in the function literal argument passed to
forEach, and these operators apply to the loop marked with
See This expressions.