inline_result 2.0.0 copy "inline_result: ^2.0.0" to clipboard
inline_result: ^2.0.0 copied to clipboard

This package brings a Kotlin-like Result<T> to Dart, using extension types for zero-cost wrapping.

Inline Result πŸš€ #

Welcome to Inline Result – your new best friend for functional error handling in Flutter/Dart. If you're coming from Android and miss Kotlin's slick Result type, this package is here to save you from messy try/catch blocks and help you write safer, cleaner code.

This package brings a Kotlin-like Result<T> to Dart, using extension types for zero-cost wrapping.

✨ Features #

  • βœ… Functional Error Handling – Chain transformations without losing control.
  • βœ… Zero-Cost Wrapping – Uses Dart extension types, meaning no extra objects at runtime.
  • βœ… Familiar API – Inspired by Kotlin's Result<T>, but Dart-friendly.
  • βœ… Safe & Readable – No more null checks or exceptions hiding in logs.

Why Inline Result? #

Flutter/Dart lacks a built-in, functional way to handle errors like Kotlin. With Dart Result, you get a familiar and robust API to:

  • Avoid nested try/catch blocks
  • Chain operations declaratively
  • Embrace immutability and safer coding practices

⚑ Quick Comparison: Kotlin vs Dart #

Kotlin #

fun divide(a: Int, b: Int): Result<Int> {
    return runCatching { a / b }
}

val result = divide(10, 2)
    .map { it * 2 }
    .getOrElse { -1 }

println(result) // 10
copied to clipboard

Dart #

Result<int> divide(int a, int b) {
  return runCatching(() => a ~/ b);
}

final result = divide(10, 2)
    .map((value) => value * 2)
    .getOrElse((e, st) => -1);

print(result); // 10
copied to clipboard

πŸ—οΈ What’s Inlining & Why Should You Care? #

Kotlin's Result<T> is an inline class, meaning it avoids extra allocations while wrapping values. Dart doesn’t have inline classes, but extension types do something similar:

extension type Result<T>(T _value) { ... }
copied to clipboard

With this, your Result<T> doesn’t create an extra objectβ€”it’s just a wrapper at compile-time. This means no native performance and runtime overhead. πŸš€

πŸ› οΈ Usage Examples #

βœ… Basic Success & Failure #

final success = Result.success("Yay!");
final failure = Result.failure(Exception("Oops!"));

print(success.getOrNull); // "Yay!"
print(failure.getOrNull); // null
copied to clipboard

πŸ”— Chaining with map and recover #

final result = runCatching(() => int.parse("42"))
    .map((value) => value * 2)
    .recover((_) => 0);

print(result.getOrThrow); // 84
copied to clipboard

πŸ”₯ Handling Failures Gracefully #

final result = runCatching(() => int.parse("NaN"))
    .getOrDefault(-1);

print(result); // -1
copied to clipboard

πŸ’€ Future Extensions #

You can easily handle asynchronous operations with our Future extensions. Convert a Future<T> into a Future<Result<T>> to chain transformations and error handling without verbose try/catch blocks. Or use asyncRunCatching.

Example:

Future<Result<Post>> fetchPost() {
  return http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'))
      .asResult()
      .map(Post.fromJson);
}

Future<String> getCurrentTitle() {
  return fetchPost()
      .onFailure((exception, stacktrace) => print('$exception; $stacktrace'))
      .map((post) => post.title)
      .recover((_, __) => 'Something went wrong')
      .getOrThrow;
}
copied to clipboard

As you can see, we have repeated each extension method for Future<Result<T>> to make it easier for you to work with the results of asynchronous operations.

⁉️ Why does Result only catch Exceptions? #

The problem is that dart error system not perfect, Error and Exception does not have single parent. And since Error is a class that should not be caught, we decided to keep only Exception.

Feel free to implement runErrorCatching or runObjectCatching in your project and use it. πŸ”₯

⏲️ Benchmarks #

We conducted several benchmarks comparing three implementations of our Result type:

  • Inline Result: Our extension type implementation.
  • Sealed with Extensions: The sealed implementation using extension methods.
  • Sealed with Pattern Matching: The sealed implementation that leverages in-place pattern matching.
Method Inline Result (us) Sealed with Extensions (us) Sealed with Pattern Matching (us)
getOrNull 12.5148 81.8594 12.3652
map 12.3678 131.572 12.3636
fold 12.3604 91.9316 12.3607

Key Observations #

  • Inline Result consistently shows excellent performance, matching the speed of the sealed implementation with in-place pattern matching.
  • All benchmarks were executed using const constructors. Notably, removing const constructors only further degrades the performance of the sealed with extensions implementation.
  • Benchmarks was run with AOT exe.
  • You can see benchmarks code at bench folder

In summary, using inline classes provides the speed benefits of pattern matching while still offering convenient extension methods such as map, fold, and getOrNull.

❀️ Contributing #

If you have ideas, improvements, or just want to say Result.success("hello!"), feel free to open an issue or PR!

4
likes
160
points
294
downloads

Publisher

unverified uploader

Weekly Downloads

2024.09.29 - 2025.04.13

This package brings a Kotlin-like Result<T> to Dart, using extension types for zero-cost wrapping.

Repository (GitHub)
View/report issues

Topics

#result #inline #either #errors

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

meta

More

Packages that depend on inline_result