1. Overview

Functions are first-class citizens in Kotlin. It’s possible to declare local functions or function literals as part of a lambda expression. Consequently, we may end up with functions inside other functions in many situations.

In this quick tutorial, we’re going to see how we can return out of a particular function in such nested function structures.

2. Return at Label

By default, the return expression in Kotlin returns from the nearest enclosing function. For instance:

fun <T> List<T>.findOne(x: T): Int {
    forEachIndexed { i, v ->
        if (v == x) {
            return i
        }
    }

    return -1;
}

In the above example, the return i expression will return to the caller of the findOne function.

Sometimes, however, we might need to return from the lambda expression, not the enclosing function. To do that, we can use the [email protected] syntax. By default, the label is the name of the function accepting the lambda – forEachIndexed in this case:

fun <T> List<T>.printIndexOf(x: T) {
    forEachIndexed { i, v ->
        if (v == x) {
            print("Found $v at $i")
            [email protected] // return out of the lambda
        }
    }
}

This approach is known as the qualified return, return at label, or even [email protected]. Instead of returning to the printIndexOf() caller, the [email protected]” will only return from the lambda expression and continue the execution of the enclosing function.

It’s also possible to use a custom label instead of the function name:

fun <T> List<T>.printIndexOf2(x: T) {
    forEachIndexed [email protected]{ i, v ->
        if (v == x) {
            print("Found $v at $i")
            [email protected]
        }
    }
}

As shown above, we renamed the default label to loop. To do that, we have to put a [email protected] before the open parenthesis of the lambda expression.

Interestingly, this approach will work even if we have multiple levels of nested lambda functions:

fun <T> List<List<T>>.nestedFind(x: T) {
    forEach { 
        it.forEachIndexed { i, v -> 
            if (v == x) {
                println("Found $v at $i")
                [email protected]
            }
        }
    }
}

As shown above, the [email protected] will return out of the outer forEach function.

As a final note, it’s possible to use standard return expressions by replacing lambda expressions with anonymous functions:

fun <T> List<T>.printIndexOfAnonymous(x: T) {
    forEachIndexed(fun(i: Int, v: T) {
        if (v == x) {
            print("Found $v at $i")
            return
        }
    })
}

This way, the nearest enclosing function will be the anonymous function, so there is no need for a qualified return.

3. Conclusion

In this short tutorial, we saw how we could control the return expression behavior in nested function structures in Kotlin.

As usual, all the examples are available over on GitHub.

guest
0 Comments
Inline Feedbacks
View all comments