Djuro
03/08/2024, 10:16 AMsuspend functions?
I know that try/catch and runCatching swallow the CancellationException .
Do you recommend wrapping the exception within coroutineScope which rethrows the exceptions at the call site like this
runCatching {
coroutineScope {/*code*/}
}.OnFailure {
// handle
}
I also use runSuspendCatching in some projects, what do you think about that approach?Dmitry Khalanskiy [JB]
03/08/2024, 10:24 AMtry/catch doesn't swallow exceptions if you only catch the exceptions that you need to handle.Joffrey
03/08/2024, 12:59 PMrunCatching as delicate? More and more people seem to try to use it for railway programming, and don't realize they are catching too much with it. It seems we made it too easy to do the wrong thing with this function.Djuro
03/08/2024, 1:11 PMDmitry Khalanskiy [JB]
03/08/2024, 2:13 PMI should always be aware of which exceptions I am handling?Yes, sure. When you are asking the program to do something, why could you not care about why it failed to do so? There are some rare situations like "I'm asking the program to do something, and it would be nice if it did that, but I don't actually care," but they are rare.
when I just don't care what type of exception it isTough to imagine this situation. Do you really not care if you get an
OutOfMemoryError? StackOverflowError? ThreadDeath? runCatching will catch all of them, even though the only option when you receive them is usually to crash the program.
What about cases when working with some old api that's not documented wellThis question is too abstract, tough to say anything definite. However, it's tough for me to imagine why you wouldn't care about why some API failed. Let's say that, during testing, you found out that the undocumented function
startServer can fail with IllegalArgumentException and IllegalStateException. You add try-catch blocks for these scenarios. Then, in a month, suddenly, this API crashes with UnsupportedOperationException each time it's called on some device. Do you really not care that this function failed with something you didn't anticipate?Dmitry Khalanskiy [JB]
03/08/2024, 2:15 PMcatch (e: Throwable) and a dozen calls to runCatching in kotlinx-coroutines. There are some cases where this is the best or even the only choice. But using runCatching should be a deliberate choice, not the default option.Dmitry Khalanskiy [JB]
03/08/2024, 2:40 PMPablichjenkov
03/08/2024, 2:41 PMpublic inline fun <T, R> T.cooperativeCatch(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
Result.failure(e)
}
}Dmitry Khalanskiy [JB]
03/08/2024, 2:45 PMcooperativeCatch { deferred.await() } pattern (where deferred is a Deferred<T>): https://github.com/Kotlin/kotlinx.coroutines/issues/1814#issuecomment-1943224856Pablichjenkov
03/08/2024, 2:50 PMCLOVIS
03/08/2024, 4:34 PMJoffrey
03/08/2024, 5:27 PMrunCatching is mostly an anti-pattern in general, and coroutine exceptions are just one obvious example. People start to ask about band-aids for `runCatching`+coroutines but the problem is more general.Djuro
03/08/2024, 6:12 PMDjuro
03/08/2024, 6:16 PMrunCatching is an anti pattern in general, how isnt try/catch also an anti pattern?CLOVIS
03/08/2024, 6:17 PMcatch(e: Throwable) is the exact same anti-pattern as runCatching. catch(e: TheSpecificExceptionIKnowThisCodeMayThrow) is fine.
The anti-pattern isn't catch itself, it's “catching absolutely anything that could happen indescriminately”. Much like having variables of type Any is an anti-pattern but any other type is fine. Even non-concrete types like `List`are fine because they still communicate intent.Djuro
03/08/2024, 6:21 PMjuliocbcotta
03/09/2024, 2:06 AMCLOVIS
03/09/2024, 4:12 PMI may be "downloading a file that is too big to fit in memory", should my app crash or should I just present an error to the user?Catching the
OutOfMemoryException will not stop the app from crashing. When OOM is thrown, it's very likely that anything you do will still lead to the app crashing, unless you are able to free a lot of memory directly in the catch block (and even then, I'm unsure it's enough for the JVM to continue working). This is what the previous users were saying: you cannot realistically recover from most non-Exception throwables. You may say you want to, but runCatching doesn't let you do that.
runcatching is not an ant pattern, it does what we wantit doesn't.
we now need a specialization of it to handle coroutines better.Use either.catch.
Dmitry Khalanskiy [JB]
03/09/2024, 5:36 PMrunCatching is okay, or the app crashes. But that's a false dichotomy. The use case you are describing (having global catch (e: Throwable) around a huge block of logic) is only a tiny portion of error handling, and having a bit more ceremony around these parts (explicitly writing out try/catch blocks, with comments that this is intentional, maybe wrapped in some code to retry the operation a couple of times) shouldn't be a problem. The issue with runCatching is that it's a shorthand version of something that should not be taken lightly.
There are use cases for wiping out all user data on your PC. There should be a way to do so. It would still be surprising if there was a dedicated button without a "danger" label on the side of your PC that did so.juliocbcotta
03/09/2024, 7:37 PMjuliocbcotta
03/09/2024, 7:41 PMjuliocbcotta
03/09/2024, 7:43 PM