crossinline in Kotlin
Kotlin
crossinline in Kotlin
Akshay Nandwana
December 30, 2024
5 min read
18 views

When working with inline functions, you may encounter crossinline. This keyword is a subtle but important modifier that ensures the safety and predictability of your code when dealing with higher-order functions. In this blog, we’ll explore what crossinline is, why it exists, and how to use it effectively.

What is crossinline?

When a function is marked as inline, it allows its lambdas to be inlined into the calling code. However, if a lambda inside an inline function is passed to another function or launched in a different execution context (e.g., a coroutine or a different thread), it might escape the scope of the calling function. This can lead to unpredictable behavior, such as returning from the wrong context.

crossinline is a modifier that prevents non-local returns from a lambda. In simple terms, it ensures that a lambda passed to an inline function cannot use the return keyword to break out of the outer function.

kotlin
inline fun executeTask(crossinline task: () -> Unit) { }

Why Use crossinline?

When working with higher-order functions, you might want to:

  1. Prevent Non-local Returns: Ensure that the lambda passed to your function doesn’t accidentally disrupt the flow of the calling function.

  2. Enforce Predictability: Restrict the lambda to behave predictably within its execution context.

  3. Avoid Scope Escaping: Safeguard your code when a lambda might escape the intended scope due to multi-threading or coroutine contexts.

Usage of crossinline

Here’s an example to understand how crossinline works:

Without crossinline

kotlin
inline fun execute(task: () -> Unit) {
    task() // Call the lambda
}

fun main() {
    execute {
        println("Before return")
        return // Non-local return: Exits from `main`
    }
    println("This will never be printed")
}
javascript
Before return

In this case, the return inside the lambda causes a non-local return, which exits the main function entirely. The code after the execute call is never executed.

With crossinline

kotlin
inline fun executeTask(crossinline task: () -> Unit) {
    task() // Call the lambda
}

fun main() {
    executeTask {
        println("Before return")
        // return  // Compiler error: Non-local returns are not allowed
    }
    println("This will be printed")
}
javascript
Before return
This will be printed

By adding crossinline, the compiler enforces that the lambda cannot use non-local returns, making the code safer and more predictable.

Common Use Cases of crossinline

1. Lambdas in Multi-threaded Environments

When a lambda is passed to a thread or coroutine, crossinline ensures it cannot break out of the calling scope unexpectedly.

kotlin
inline fun runInThread(crossinline action: () -> Unit) {
    Thread {
        action() // Safely executes without non-local returns
    }.start()
}

fun main() {
    runInThread {
        println("Running in a separate thread")
        // return // Compiler error
    }
}

2. Chained Higher-order Functions

When chaining multiple higher-order functions, crossinline ensures predictable behavior for each lambda.

kotlin
inline fun process(crossinline action: () -> Unit) {
    println("Before action")
    action()
    println("After action")
}

fun main() {
    process {
        println("Inside action")
        // return // Compiler error
    }
    println("End of main")
}
javascript
Before action
Inside action
After action
End of main

When to Use crossinline?

Use crossinline when:

  • Your lambda is passed to another function or a different thread, and you want to prevent scope-related issues.

  • Non-local returns from the lambda could lead to unpredictable or undesired behavior.

  • You want to enforce stricter control over the lambda's behavior within the inline function.

Conclusion

  • inline Functions: Allow the compiler to copy the function body directly into the call site, improving performance and reducing overhead.

  • crossinline: Ensures that lambdas cannot use non-local returns, adding an extra layer of safety and predictability to your code.

  • Use crossinline thoughtfully in scenarios involving concurrency, multi-threading, or scope-sensitive operations.

Also check:
Noinline in Kotlin - https://www.androidengineers.in/blogs/power-of-noinline-in-kotlin-8yp9fs
Inline in Kotlin - https://www.androidengineers.in/blogs/inline-functions-in-kotlin-ynr1zb

Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
https://www.androidengineers.in/courses

Share This Article
Stay Updated

Get the latest Android development articles delivered to your inbox.