Search code examples
kotlinlambdainline

A 'return' expression required in a function with a block body despite inlined lambda which has the return


I'm trying to create an inlined try-catch helper function but running into a compilation error due to the return statement occurring within the inlined lambda. Below is some code demonstrating the same issue

fun isStringEmpty(myString: String): Boolean {
    stringOpHelper { 
        return myString.length == 0
    }
}
inline fun <T> stringOpHelper(fn: () -> T) {
    println("performing string operation")
    fn()
}

This will compile with the desired effect if a return is added after the inlined function call or an exception is thrown, but that code should be unreachable. Eg:

fun isStringEmpty(myString: String): Boolean {
    stringOpHelper { 
        return myString.length == 0
    }
    TODO("this is actually unreachable")
}
inline fun <T> stringOpHelper(fn: () -> T) {
    println("performing string operation")
    fn()
}

My expectation was that the compiler would see that stringOpHelper always calls fn() and the fn in isStringEmpty always returns, so the inlined stringOpHelper call always returns.

Is it possible to define the inline helper function in a way that avoids the need for the unreachable exception / return in the calling function? Otherwise, what's the reason for why this isn't possible?


Solution

  • There is mechanism for such purposes called contracts, but this feature is experimental and its usage must be marked with @ExperimentalContracts or @OptIn(ExperimentalContracts::class)

    @OptIn(ExperimentalContracts::class)
    inline fun <T> stringOpHelper(fn: () -> T) {
        contract {
            callsInPlace(fn, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
        }
        println("performing string operation")
        fn()
    }