androidengineers.Book a session
← All articles

Kotlin

crossinline in Kotlin

Enhancing Safety in Inline Functions

By Akshay Nandwana5 min read
crossinline in Kotlin

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