CodeBlanks Logo Icon CodeBlanks

Result pattern

Learn functional error handling with the Result pattern.

Result pattern

Returning errors as values instead of throwing exceptions is a functional approach. This is called the Result pattern.

In Dart you typically define a sealed class Result<T>: Success<T>(T value) and Failure<T>(String error). The caller splits the value with pattern matching.

This handles errors in a type-safe way; a forgotten error case becomes a compile error.

A Result pattern example

A typical divide function: Result<int> divide(int a, int b). If b is zero it returns Failure('division by zero'), otherwise Success(a ~/ b).

The caller: switch (result) { case Success(:final value): print(value); case Failure(:final error): print(error); }.

Thanks to sealed the compiler checks every case; adding a new subclass warns all switches.

Practice Divide with Result

Define sealed class Result<T> {}. Add class Success<T> extends Result<T> (final T value + constructor) and class Failure<T> extends Result<T> (final String error + constructor). Write Result<int> divide(int a, int b): if b is 0 return Failure('division by zero'), else Success(a ~/ b). In main call divide(10, 0) and split with switch: print value on Success, error on Failure.

Starter code

main.dart
// Define the Result, Success, Failure classes

// Write the divide function

void main() {
  // call divide(10, 0), split with switch
}

Sample solution

main.dart
sealed class Result<T> {}

class Success<T> extends Result<T> {
  final T value;
  Success(this.value);
}

class Failure<T> extends Result<T> {
  final String error;
  Failure(this.error);
}

Result<int> divide(int a, int b) {
  if (b == 0) return Failure('division by zero');
  return Success(a ~/ b);
}

void main() {
  final r = divide(10, 0);
  switch (r) {
    case Success(:final value):
      print(value);
    case Failure(:final error):
      print(error);
  }
}

Expected output

division by zero

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the advantage of the Result pattern?

  • Ignores errors
  • Makes errors type-safe Correct answer
  • Runs faster
  • Removes Exception

Explanation: the Result pattern represents each error case with a type.

2 Access the Success value

Question
switch (result) { case Success(:final value): print(____); }
Answer
switch (result) { case Success(:final value): print(value); }

Explanation: the Success pattern binds value.

3 Result pattern parts

Term Meaning
Success<T> Success case
Failure<T> Failure case
sealed class Closed hierarchy
switch Pattern matching

All lessons in Intermediate