Kotlin’s crossinline keyword

Problem: non-local return

Let’s first understand the problem of non-local return with a simple example:

fun doSomething() {
    println("Before lambda")
    doSomethingElse {
        println("Inside lambda")
        return // This is non-local return
    }
    println("After lambda")
}

inline fun doSomethingElse(lambda: () -> Unit) {
    println("Do something else")
    lambda()
}

Non-local return

In the code above, the return statement is called a non-local return because it’s not local to the function in which it is called. This means this return statement is local to the doSomething() function and not to the lambda function in which it is called. So, it terminates the current function as well as the outermost function.

Local return

If you just wanted to return from the lambda, you would say return@doSomethingElse. This is called local return and it is local to the function where it is specified.

Problem

Now the problem here is that the compiler skips the lines after the non-local return statement. The decompiled bytecode for the doSomething() looks like following:

public static final void doSomething() {
    System.out.println("Before lambda");
    System.out.println("Doing something else");
    System.out.println("Inside lambda");
}

Notice that there is no statement generated for the line println("After lambda"). This is because we have the non-local return inside the lambda and the compiler thinks the code after the return statement is meaningless.


Solution: crossinline keyword

crossinline

In such cases (like the problem mentioned above), the solution is to disallow the non-local return inside the lambda. To achieve this, we mark the lambda as crossinline:

inline fun doSomethingElse(crossinline lambda: () -> Unit) {
    println("Doing something else")
    lambda()
}

Non-local return disallowed

When you use the crossinline keyword, you are telling the compiler, “give me an error, if I accidentally use a non-local return inside the nested functions or local objects.”:

fun doSomething() {
    println("Before lambda")
    doSomethingElse {
        println("Inside lambda")
        return                  // Error: non-local return
        return@doSomethingElse  // OK: local return
    }
    println("After lambda")
}

Now the compiler generates the bytecode as expected:

public static final void doSomething() {
    System.out.println("Before lambda");
    System.out.println("Doing something else");
    System.out.println("Inside lambda");
    System.out.println("After lambda");
}

That’s it! Hope I made it easier to understand.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)