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.
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) { }
crossinline
?When working with higher-order functions, you might want to:
Prevent Non-local Returns: Ensure that the lambda passed to your function doesn’t accidentally disrupt the flow of the calling function.
Enforce Predictability: Restrict the lambda to behave predictably within its execution context.
Avoid Scope Escaping: Safeguard your code when a lambda might escape the intended scope due to multi-threading or coroutine contexts.
crossinline
Here’s an example to understand how crossinline
works:
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.
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.
crossinline
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
}
}
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
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.
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:
Join our upcoming classes
https://www.androidengineers.in/courses