skip to content

What is Kotlin's Result<T> type and what does runCatching { } do?

level: juniorimportance: must knowfreq 60%

answer

  1. Success holds T, failure holds Throwable
  2. runCatching = try/catch that returns a Result
  3. getOrNull / exceptionOrNull / getOrThrow
  4. Result.success / Result.failure factories
  5. @JvmInline value class — cheap wrapper

basics

~10 s

Result<T> is a box that holds either a successful value or a failure with an exception. runCatching runs a block of code and returns success if it finishes, or failure if it throws.

solid answer

~30 s

Result<T> is a stdlib type that represents the outcome of an operation: either a success holding a value of type T, or a failure holding a Throwable. You create one with Result.success(value), Result.failure(throwable), or runCatching { }. runCatching executes the lambda and wraps a normal return in Result.success, or any thrown exception in Result.failure. You inspect it with isSuccess / isFailure, getOrNull(), exceptionOrNull(), or getOrThrow(). It lets you treat errors as values instead of letting exceptions propagate immediately, which is handy at boundaries where you want to defer or transform error handling.

code

kotlin · 8 lines
kotlin
fun readPort(raw: String): Result<Int> = runCatching {
    val n = raw.toInt()
    require(n in 1..65535) { "port out of range" }
    n
}

val ok = readPort("8080").getOrNull()        // 8080
val bad = readPort("99999").exceptionOrNull() // IllegalArgumentException

go deeper

for a junior

Knows Result is success-or-failure and runCatching wraps a try/catch into a returned value.

for a middle

Fluently uses getOrNull/getOrElse/exceptionOrNull and both runCatching forms, and knows the factory functions.

for a senior

Mentions Result is an inline value class and frames it as 'errors as values' for boundary code.

for a principal

Can position Result against exceptions and sealed-class result types and discuss API design implications.

## What Result<T> is `Result<T>` is a class in the Kotlin standard library (`kotlin.Result`) that encapsulates the outcome of an operation as a value. It is in exactly one of two states: - **Success** — holds a value of type `T`. - **Failure** — holds a `Throwable` (the exception that occurred). This turns error handling into data: instead of an exception unwinding the stack, you get back an object you can pass around, inspect, and transform. ## Creating a Result - `Result.success(value)` — wraps a value. - `Result.failure(throwable)` — wraps an exception. - `runCatching { ... }` — the most common factory. It runs the lambda; if it returns normally, you get `Result.success(returnValue)`; if it throws, you get `Result.failure(thrown)`. There is also a receiver form: `someObject.runCatching { ... }`, where `this` inside the block is `someObject`. ## Reading a Result - `isSuccess` / `isFailure` — booleans. - `getOrNull()` — the value, or `null` if failure. - `exceptionOrNull()` — the exception, or `null` if success. - `getOrThrow()` — the value, or re-throws the captured exception. - `getOrDefault(default)` / `getOrElse { e -> ... }` — value or a fallback. ```kotlin fun parse(text: String): Result<Int> = runCatching { text.toInt() } val r = parse("42") println(r.isSuccess) // true println(r.getOrNull()) // 42 val bad = parse("oops") println(bad.getOrNull()) // null println(bad.exceptionOrNull()) // java.lang.NumberFormatException println(bad.getOrDefault(-1)) // -1 ``` ## Why it is an inline value class `Result` is declared as a `@JvmInline value class`. At runtime there is usually no extra object allocation for the wrapper itself — the value or a small failure holder is used directly. This keeps it cheap. ## Key idea Result lets you **delay or relocate** the decision about what to do with an error: capture it now, decide later. That is its main purpose at code boundaries.

  • How do you get the value out while supplying a fallback if it failed?
    Use getOrElse { e -> fallback } or getOrDefault(value); getOrElse gives you the exception to branch on, getOrDefault just substitutes a constant.
  • What does getOrThrow() do on a failure?
    It re-throws the original captured Throwable, so you go back to ordinary exception-based control flow.

A Result is like a sealed envelope: open it and you find either the prize (value) or a note explaining what went wrong (exception).

saying these in an interview costs you the question

  • Thinking Result can hold both a value and an exception at once
  • Saying runCatching swallows errors silently with no way to retrieve them
  • Confusing Result<T> with a nullable T (null doesn't carry an exception)
  • Believing runCatching catches compile-time errors rather than runtime exceptions

context