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.

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

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")
}
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

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")
}
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.

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.

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")
}
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

Love from our past students

Excellent list of questions really helped me to coverup all the topics before interview.

Saiteja Janjirala

10th Oct, 2024

I had an exceptional experience with the 1:1 mentorship session. Akshay was incredibly friendly and provided invaluable guidance on focusing on long-term goals. They also gave great interview tips, including a thorough resume review. Additionally, the discussion on Data Structures and Algorithms (DSA) was insightful and practical. Highly recommended for anyone looking to advance their career!

Nayab khan

11th Sep, 2024

Cleared my major points for what I am missing in the resume and also suggested what I can work on for further growth in the career.

Ketan Chaurasiya

7th Aug, 2024

What impressed me most was his personalized approach and practical tips that I could immediately apply. Akshay’s guidance not only improved my technical skills but also boosted my confidence in navigating my career path. His insights and encouragement have been a game-changer for me. I highly recommend Akshay’s mentorship to anyone looking to advance their Android development career.

Hardik Kubavat

5th Aug, 2024

2025© Made with   by Android Engineers.