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 return@label 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")
            return@forEachIndexed // return out of the lambda
        }
    }
}

This approach is known as the qualified return, return at label, or even return@. Instead of returning to the printIndexOf() caller, the “return@forEachIndexed” 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 loop@{ i, v ->
        if (v == x) {
            print("Found $v at $i")
            return@loop
        }
    }
}

As shown above, we renamed the default label to loop. To do that, we have to put a label@ 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")
                return@forEach
            }
        }
    }
}

As shown above, the return@forEach 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.

Comments are closed on this article!