Explain `Result<T>` in the Kotlin stdlib: how it represents success/failure, how to create and consume it, and its key limitations.
answer
- inline value class: value or Throwable
- runCatching / success / failure
- fold, getOrElse, map vs mapCatching
- runCatching swallows CancellationException
- error channel is untyped Throwable
basics
~10 sResult<T> holds either a successful value or a thrown error, without using exceptions to control flow. You can check which one it is and transform the value safely.
solid answer
~40 s`Result<T>` is an inline value class wrapping either a success value or a `Throwable`. Create it with `Result.success(v)`, `Result.failure(e)`, or `runCatching { ... }`, which catches exceptions and wraps them. Consume it with `getOrNull()`, `getOrDefault()`, `getOrElse { }`, `getOrThrow()`, `exceptionOrNull()`, or `fold(onSuccess, onFailure)`. Transform with `map`, `mapCatching`, `recover`, and side-effect with `onSuccess`/`onFailure`. Because it is a `@JvmInline value class`, a successful result avoids boxing in many cases. Key limitations: by convention you should not use `Result` as a function return type or parameter pre-2.x (the compiler historically restricted it; `kotlin.Result` is best for local/internal flows), `runCatching` catches *all* `Throwable` including `CancellationException`, which is dangerous in coroutines, and it carries no typed error channel — the error is just `Throwable`.
code
kotlin · 3 linesval parsed: Int = runCatching { input.toInt() }
.onFailure { log.warn("bad input", it) }
.getOrDefault(0)go deeper
Knows Result holds success-or-failure and you can get the value or a default.
Creates with runCatching, consumes with fold/getOrElse, and names map/recover.
Explains the inline value class, mapCatching vs map, and the CancellationException pitfall.
Weighs Result vs sealed/Either for API contracts, knows the public-return-type restriction, and reasons about error-channel typing trade-offs.
## What `Result<T>` is `kotlin.Result<T>` is a stdlib type that holds **one of two outcomes**: a **success** carrying a value of type `T`, or a **failure** carrying a `Throwable`. It lets you treat errors as ordinary return data (a 'railway' / monadic style) instead of unwinding the stack with exceptions. It is declared as a `@JvmInline value class` wrapping an `Any?`. Internally a failure is stored as a small `Failure` holder containing the exception, and a success stores the raw value. Being an inline value class means successes often avoid heap allocation/boxing. ## Creating a Result ```kotlin val a = Result.success(42) val b = Result.failure<Int>(IllegalStateException("boom")) val c = runCatching { riskyParse(input) } // catches throwables, wraps them ``` `runCatching { }` is the workhorse: it runs the block and returns `Result.success` or, if anything is thrown, `Result.failure`. There is also a receiver form `x.runCatching { ... }`. ## Inspecting / consuming - `isSuccess` / `isFailure` — booleans. - `getOrNull()` — value or `null`. - `exceptionOrNull()` — the throwable or `null`. - `getOrThrow()` — value or rethrows the stored exception. - `getOrDefault(d)` / `getOrElse { e -> ... }` — fallback on failure. - `fold(onSuccess = { }, onFailure = { })` — handle both branches and return a value. ## Transforming - `map { }` — transform the success value (does **not** catch exceptions in the transform). - `mapCatching { }` — like `map` but catches exceptions thrown by the transform into a failure. - `recover { e -> ... }` / `recoverCatching` — turn a failure back into a success. - `onSuccess { }` / `onFailure { }` — side effects, return the same `Result`. ```kotlin val len: Int = runCatching { fetch() } .map { it.body } .recover { "" } .getOrThrow() .length ``` ## Important limitations / gotchas - **Coroutines**: `runCatching` catches **all** `Throwable`, including `kotlinx.coroutines.CancellationException`. Swallowing cancellation breaks structured concurrency, so inside coroutines prefer explicit `try/catch` that rethrows `CancellationException`. - **No typed error**: the failure channel is just `Throwable`; it cannot express a closed set of domain errors like a `sealed class` can. For rich domain modeling, a `sealed`/`Either`-style type is often better. - **API design**: `kotlin.Result` was historically restricted as a public function return type (compiler-enforced), so it is most idiomatic for local/internal composition rather than as a published API contract. - `map` vs `mapCatching`: `map` lets exceptions from the lambda escape; `mapCatching` re-wraps them. ## Why it fits the 'stdlib model' theme Kotlin maps the cross-language concept of an **error-as-value monad** onto a single inline class plus a family of extension functions, instead of a language feature. This keeps exceptions available while offering a functional alternative when you want one.
- Why is runCatching dangerous inside a coroutine?It catches CancellationException, swallowing cancellation and breaking structured concurrency; rethrow it or use targeted try/catch.
- What's the difference between map and mapCatching?map lets exceptions from the transform propagate; mapCatching wraps them into a Result.failure.
saying these in an interview costs you the question
- Saying Result can hold a typed domain error instead of just Throwable
- Using runCatching in coroutine code without handling CancellationException
- Confusing map (no catch) with mapCatching (catches)
- Claiming getOrThrow returns null on failure
- Treating Result as a free replacement for sealed-class domain errors