← Back to Kotlin Course | Chapter 13: Error Handling & Testing | Lesson 3 of 6

The Result Type

The Result type is a box that either holds a successful answer or holds the error that happened instead, without ever throwing.

Creating a Result with runCatching

runCatching { } executes a block of code and wraps the outcome in a Result: a caught exception becomes a failure, and a normal return value becomes a success.

Example: Creating a Result with runCatching

markup
fun main() {
    val result = runCatching { "42".toInt() }
    println("Success: ${result.isSuccess}")
}

Extracting Values Safely

.getOrNull() returns the successful value or null on failure, while .getOrElse { } lets you supply a fallback computed from the failure itself.

Example: Extracting Values Safely

markup
fun main() {
    val goodResult = runCatching { "42".toInt() }
    val badResult = runCatching { "abc".toInt() }
    println("Good: ${goodResult.getOrNull()}")
    println("Bad with fallback: ${badResult.getOrElse { -1 }}")
}

Handling Both Outcomes with fold

.fold(onSuccess, onFailure) lets you handle a success and a failure case together in one expression, producing a single unified result from either branch.

Example: Handling Both Outcomes with fold

markup
fun main() {
    val result = runCatching { "abc".toInt() }
    val message = result.fold(
        onSuccess = { "Parsed: $it" },
        onFailure = { "Failed: ${it.message}" }
    )
    println(message)
}

Result in Function Return Types

A function can return Result<T> directly instead of throwing, making failure an explicit, visible part of its return type that callers must consciously handle. (Declaring Result as an explicit function return type requires Kotlin 1.5+; on older versions the same value can still be computed and stored in a val, as shown below.)

Example: Result in Function Return Types

markup
fun main() {
    val input = "-5"
    val result: Result<Int> = runCatching {
        val age = input.toInt()
        require(age >= 0) { "Age cannot be negative" }
        age
    }
    println(result.fold(onSuccess = { "Age: $it" }, onFailure = { "Invalid: ${it.message}" }))
}
Common Mistakes
  1. Forgetting Result is designed as a return-value alternative to exceptions, not a replacement for using try/catch when calling code that itself throws.
  2. Not checking .isSuccess/.isFailure (or using .fold/.getOrElse) and instead calling .getOrThrow() immediately, defeating the purpose of a non-throwing result.
  3. Assuming Result can hold any kind of computation state (like 'still loading'); it strictly represents success or failure, not other states.
Chapter Summary
  • Result<T> represents either a successful value or a failure, without needing a try/catch at the call site.
  • runCatching { } runs a block and wraps its outcome in a Result, catching any thrown exception into the failure case.
  • .getOrNull(), .getOrElse { }, and .getOrThrow() extract the value from a Result in different ways.
  • .fold(onSuccess, onFailure) handles both outcomes of a Result in a single expression.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.