anyhow 0.4.0 copy "anyhow: ^0.4.0" to clipboard
anyhow: ^0.4.0 copied to clipboard

Inspired by Rust's "anyhow" crate, this Dart package provides versatile error handling, implementing Rust's Result monad and "anyhow" crate functionality

anyhow #

Pub Version Dart Package Docs License: MIT

Taking inspiration from the Rust crate of the same name, "anyhow", this Dart package offers versatile and idiomatic error handling capabilities.

"anyhow" not only faithfully embodies Rust's standard Result monad type but also brings the renowned Rust "anyhow" crate into the Dart ecosystem. You can seamlessly employ both the Result type and the Anyhow Result type, either in conjunction or independently, to suit your error-handling needs.

Intro to Usage #

Regular Dart Error handling #

void main() {
  try {
    print(order());
  } catch(e) {
    print(e);
  }
}

String order() {
  final user = "Bob";
  final food = "pizza";
  makeFood(food);
  return "Order Complete";
}

String makeFood(String order) {
  return makeHamburger();
}

String makeHamburger() {
  // Who catches this??
  // How do we know we won't forget to catch this??
  // What is the context around this error??
  throw "Hmm something went wrong making the hamburger.";
}

Output

Hmm something went wrong making the hamburger.

The Better Ways To Handle Errors With Anyhow #

What Is a Result Monad Type And Why Use it?

A monad is just a wrapper around an object that provides a standard way of interacting with the inner object. The Result monad is used in place of throwing exceptions. Instead of a function throwing an exception, the function returns a Result, which can either be a Ok (Success) or Err (Error/Failure), Result is the type union between the two. Before using the inner object, you need to check the type of the Result through isOk() or isErr(), or you can directly unwrap() if you know an error is impossible. Doing so allows you to either resolve any potential issues in the calling function or pass the error up the chain until a function resolves the issue. This provides predictable control flow to your program, eliminating many potential bugs and countless hours of debugging.

Result Type Error Handling #

With the base Result type, there is no more undefined behaviour due to control flow.

import 'package:anyhow/base.dart';

void main() {
  print(order());
}

Result<String,String> order() {
  final user = "Bob";
  final food = "pizza";
  final result = makeFood(food);
  if(result.isOk()){
    return Ok("Order Complete");
  }
  return result;
}

Result<String,String> makeFood(String order) {
  return makeHamburger();
}

Result<String,String> makeHamburger() {
  // What is the context around this error??
  return Err("Hmm something went wrong making the hamburger.");
}
Output
Hmm something went wrong making the hamburger.

Anyhow Result Type Error Handling #

With the Anyhow Result type, we now can know and add context around errors. To do so, we can add context or withContext (lazily). Either will only have an effect if a Result is the Err subclass.

import 'package:anyhow/anyhow.dart';

void main() {
  print(order());
}

Result order() {
  final user = "Bob";
  final food = "pizza";
  final result = makeFood(food).context("$user ordered.");
  if(result.isOk()){
    return Ok("Order Complete");
  }
  return result;
}

Result<String> makeFood(String order) {
  return makeHamburger().context("order was $order.");
}

Result<String> makeHamburger() {
  return bail("Hmm something went wrong making the hamburger.");
}

Output

Error: Bob ordered.

Caused by:
	0: order was pizza.
	1: Hmm something went wrong making the hamburger.

and we can include Stack Trace with Error.hasStackTrace = true:

Error: Bob ordered.

Caused by:
	0: order was pizza.
	1: Hmm something went wrong making the hamburger.

StackTrace:
#0      new Error (package:anyhow/src/anyhow/anyhow_error.dart:36:48)
#1      AnyhowErrExtensions.context (package:anyhow/src/anyhow/anyhow_extensions.dart:46:15)
#2      AnyhowResultExtensions.context (package:anyhow/src/anyhow/anyhow_extensions.dart:12:29)
... <OMITTED FOR EXAMPLE>

and we can swap the order with Error.displayFormat = ErrDisplayFormat.stackTrace

Root Cause: Hmm something went wrong making the hamburger.

Additional Context:
	0: order was pizza.
	1: Bob ordered.

StackTrace:
#0      new Error (package:anyhow/src/anyhow/anyhow_error.dart:36:48)
#1      bail (package:anyhow/src/anyhow/functions.dart:6:14)
#2      makeHamburger (package:anyhow/test/src/temp.dart:31:10)
... <OMITTED FOR EXAMPLE>

Base Result Type vs Anyhow Result Type #

The base Result Type and the anyhow Result Type can be imported with

import 'package:anyhow/base.dart' as base;

or

import 'package:anyhow/anyhow.dart' as anyhow;

Respectively. Like in anyhow, these types have parity, thus can be used together

import 'package:anyhow/anyhow.dart' as anyhow;
import 'package:anyhow/base.dart' as base;

void main(){
  base.Result<int,anyhow.Error> x = anyhow.Ok(1); // valid
  anyhow.Result<int> y = base.Ok(1); // valid
  anyhow.Ok(1).context(1); // valid
  base.Ok(1).context(1); // not valid

}

The base Result type is the standard implementation of the Result type and the anyhow Result type is the anyhow implementation on top of the standard Result type.

Adding Predictable Control Flow To Legacy Dart Code #

At times, you may need to integrate with legacy code that may throw or code outside your project. To handle, you can just wrap in a helper function like executeProtected

void main() {
  Result<int> result = executeProtected(() => functionMayThrow());
  print(result);
}

int functionMayThrow(){
  throw "this message was thrown";
}

Output:

Error: this message was thrown

See examples for more.

18
likes
0
pub points
56%
popularity

Publisher

verified publishervoyver.com

Inspired by Rust's "anyhow" crate, this Dart package provides versatile error handling, implementing Rust's Result monad and "anyhow" crate functionality

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

meta

More

Packages that depend on anyhow