anyhow 0.4.0
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 #
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.