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
Any expression in Kotlin may be marked with a label. Labels have the from of the @ sign followed by an optional identifier, for examples @, @abc, @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.
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:
|Non-local returns are not implemented yet|
See the corresponding issue.
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 loopbody annotation:
Now, the compiler allows break and continue in the function literal argument passed to forEach, and these operators apply to the loop marked with @@.
|Break and continue for custom control structures are not implemented yet|
See the corresponding issue.
See This expressions.