What is the difference between the top-level runCatching { } and the receiver form someObject.runCatching { }, and how do you read the captured exception?
answer
- Two overloads: top-level and T.runCatching
- Receiver form: this == the object inside the block
- Both inline → no lambda allocation, non-local return allowed
- Read failure with exceptionOrNull / getOrElse / fold
- getOrThrow re-raises the original Throwable
basics
~10 sTop-level runCatching just runs a block. The receiver form runs the block with a chosen object as 'this' inside it. Both return a Result; you read the error with exceptionOrNull() or getOrElse.
solid answer
~40 sThere are two overloads. The top-level `runCatching { block }` runs the lambda with no receiver and returns `Result<R>`. The extension form `T.runCatching { block }` runs the lambda with the receiver `T` as `this`, so you can call the object's members directly, and returns `Result<R>` of whatever the block produces. Both capture a thrown exception into `Result.failure`. To read the failure you use `exceptionOrNull()` (returns the Throwable or null), `getOrElse { e -> ... }` (gives you the exception to compute a fallback), `fold(onSuccess, onFailure)`, or `getOrThrow()` to re-raise. Choose the receiver form when the block operates on a specific object (`connection.runCatching { commit() }`), and the top-level form for free-standing expressions.
code
kotlin · 7 lines// receiver form: 'this' is the StringBuilder
val text = StringBuilder().runCatching {
append("a"); append("b"); toString()
}.getOrElse { "" }
// top-level form
val n = runCatching { "7".toInt() }.exceptionOrNull() // null (success)go deeper
Knows both forms return a Result and can read the value with getOrNull.
Distinguishes the receiver rebinding of 'this' and picks the right form; uses getOrElse/fold/exceptionOrNull correctly.
Explains the inline implications (no allocation, non-local return, honest stack traces) and chooses forms for readability.
Weighs runCatching ergonomics against a domain-specific sealed result type and sets team conventions.
## The two overloads The stdlib declares `runCatching` twice: ```kotlin public inline fun <R> runCatching(block: () -> R): Result<R> public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> ``` Both are `inline`, so the lambda body is inlined at the call site — no function-object allocation, and `non-local return` from the block is allowed. ### Top-level form ```kotlin val r: Result<Int> = runCatching { compute() } ``` No receiver; `this` inside the block is whatever the enclosing scope's `this` is. ### Receiver (extension) form ```kotlin val r: Result<Unit> = file.runCatching { writeText("hi") } // ^ this == file inside the block ``` Here `T.() -> R` means the block has `file` as `this`, so you can call `writeText` without qualification. This reads naturally when the whole block is about one object. ## Reading the outcome | API | Returns | |-----|---------| | `exceptionOrNull()` | the captured `Throwable`, or `null` on success | | `getOrNull()` | the value, or `null` on failure | | `getOrElse { e -> ... }` | value, or a computed fallback (receives the exception) | | `getOrDefault(d)` | value, or constant `d` | | `fold(onSuccess, onFailure)` | result of whichever branch runs | | `getOrThrow()` | value, or re-throws the captured exception | ```kotlin val port: Int = "abc".runCatching { toInt() } .getOrElse { e -> log.warn("bad port: ${e.message}") 8080 } ``` ## Why `inline` matters here Because `runCatching` is `inline`, the captured exception's stack trace points at your real code, and there is no overhead from a lambda object. It also means you cannot accidentally `return` out of the surrounding function unexpectedly — non-local returns behave as with any inline function. ## Picking a form - Use **receiver form** when the block is a sequence of calls on one object — it removes noise. - Use **top-level form** for standalone expressions or when no single receiver dominates.
- Why is runCatching declared inline?So the block is inlined (no lambda object, allows non-local return) and the captured exception's stack trace reflects the real call site.
- When would you prefer fold over getOrElse?fold when you must transform both the success and failure branches into the same target type in one expression; getOrElse only handles the failure branch.
saying these in an interview costs you the question
- Claiming the receiver form changes what exceptions are caught
- Not knowing 'this' is rebound inside the receiver-form block
- Saying runCatching allocates a lambda on every call
- Reaching for getOrThrow when the whole point was to avoid throwing