I recently started using `Result` as a return type...
# coroutines
a
I recently started using
Result
as a return type as of Kotlin 1.5. I’m having seemingly random crashes with suspending functions that return
Result
(I haven’t checked with other inline/value classes). it’s happening in the generated
invokeSuspend
:
Copy code
Fatal Exception: java.lang.ClassCastException
java.util.ArrayList cannot be cast to kotlin.Result
I don’t have reliable repro steps for it, it seems to happen on some android devices, at different places but all the same crash, but not always 😢 I’m really stumped with it. I’ve been looking at the difference in the decompiled bytecode between suspending and non suspending functions that return
Result
, I can see casting of some
Object
to
Result
, assuming that’s what’s failing 😕
s
suspend fun foo(): A
basically desugards to
fun foo(): Result<A>
(it's a bit more complicated because of async stuff but it's the basic idea) this means it can fail with
Throwable
or succeed with
A
that means that
suspend fun bar(): <Result<A>>
desugars to
fun bar(): Result<Result<A>>
meaning it can: fail with
Throwable
(outer result), fail with
Throwable
or succeed with
A
. The recommended solution merge the two
Throwable
failures and change the return type to ``suspend fun bar(): A``. You can then use
runCatching
or
try/catch
when launching your coroutine to handle the failure
e
Any chance you could reproduce it?
a
Yea I considered unwrapping the Result's type as the return type and might be my only option. Ideally though I'd like to express that it could fail in the method signature :/
r
@elizarov. I was also seeing a similar issue and had to downgrade, Its mention that it should be resolved in kotlin 1.5.30
a
I'm gonna have to replace all usages of
Result
with a non inlined version until it's resolved. It's less hassle than unwrapping all return types. Ideally we could somehow force it not to be inlined until it's fixed. I'm using Jetpack Compose which seems to lag behind kotlin versions, so I imagine I'll need to wait a bit longer 🥲
If there was a way to easily replace all usages like with the deprecation/ replace with functionality that can import the replaced APIs that'd be great
I'm not really fussed on losing performance here, it's more about safety I care about with the
Result
API
r
You could just wait till 1.5.30 is out. It's already on its Release candidate
a
I hope it fixes it. In my case it was a class cast from T to Result<T>, not the other way around as this bug states
r
try the release canaidate and see if the issue is resolve
or you can opt out of the IR backend
a
Trying with the IR backend disabled now yea
Ok ok, just to clear this up, it’s 100% this bug here https://youtrack.jetbrains.com/issue/KT-47206
I can see that my code is doing the same,
suspend
function that returns a
Result
, but it has a parameter with a default value. removing the default value or removing the
suspend
operator works (though in my case I can’t remove the
suspend
)
Here is the minimal repro steps I found, simpler than the bug
Copy code
suspend fun main(args: Array<String>) {
    getResult(true).onSuccess { println(it) }   // A-OK
    getResult().onSuccess { println(it) }       // ClassCastException!
}

suspend fun getResult(arg: Boolean = true): Result<Boolean> = Result.success(arg)
And yes I’ve tested 1.5.21 and 1.5.30-RC, it is indeed gone in the latter