Results
What is a Result?
The Result<T, E>
type represents a computation that can either succeed with a value - Ok<T, E>
- or fail with an error - Err<T, E>
. Where Option<T>
models absence, Result<T, E>
models failure. It is how functional programming encodes errors into the type system itself, rather than throwing them into the runtime.
The Exception Problem
In traditional object-oriented code, exceptions are your go-to mechanism for error handling. But exceptions are:
Invisible in function signatures
Easy to forget to handle
Hard to compose
Catastrophic in chains
They're basically control flow with a bomb strapped to it. If a method throws, anything down the chain is at risk, and you can't tell which methods do or don't throw unless you read the source or the documentation.
Result<T, E>
bakes error handling into the type system
Functional Error Handling
With Result<T, E>
, every operation explicitly returns either success or failure:
You can
Map
over the success value without touching the errorYou can
AndThen
into further computations that also returnResult
You can
Match
orInspect
the outcome at the edge of your program - not the middle
Result<User, Error> GetUser(string id);
Result<Address, Error> GetAddress(User user);
Result<string, Error> FormatAddress(Address address);
var result = GetUser("Keyleth")
.AndThen(GetAddress)
.AndThen(FormatAddress);
If any step fails, the whole chain fails, and the error is carried forward untouched. No try/catch
. No special cases. Just clean, predictable control flow.
Intentional Errors
Unlike Option<T>
, which represents uncertainty, Result<T, E>
represents an expected failure mode. You're not just saying "this might not exist", you're saying "this might go wrong, and here's what it looks like if it does".
Result<User, Error> TryCreateUser(string input);
This makes it ideal for parsing, validation, and domain logic. You're not just opting out of exceptions - you're describing your domain more accurately.
When to Use Result
Reach for Result<T, E>
when:
A function might fail and you want to make that explicit
You want the caller to explicitly handle the failure case
You care about the reason for the failure
You want to chain operations but bail early on error
You're validating, parsing, or transforming user input
You want to reserve exceptions for real application errors
Avoid Result<T, E>
if:
You don't care about the reason for the failure (reach for
Option<T>
instead)
Summary
Result<T, E>
is a better model for failure than exceptions. It gives you structured, type-safe, and composable control flow. Instead of blowing up at runtime, your failures travel through your system like first-class citizens.
Last updated
Was this helpful?