0 purchases
anyhow
anyhow #
anyhow offers versatile and idiomatic error handling capabilities to make your code safer, more maintainable, and
errors easier to debug.
This is accomplished through the use of the Result monad type and
an implementation of the popular Rust crate with the same name - anyhow.
anyhow will allow you to never throw another exception again and have a predictable control flow. When
errors do arise, you can add context to better understand the situation that led to the errors.
See here to jump right into an example.
anyhow is built on the rust_core ecosystem, so it works well with other packages, but also works as a standalone
package.
Table of Contents #
What Is a Result Monad Type And Why Use it?
The Better Way To Handle Errors With Anyhow
Example
What Would This Look Like Without Anyhow
Configuration Options
Base Result Type vs Anyhow Result Type
What Is a Result Monad Type And Why Use it? #
If you are not familiar with the Result type, why it is needed, or it's usages, you can read up on all that here:
article
The Better Way To Handle Errors With Anyhow #
Before anyhow, with a regular Result type, we had no way to know the context around Errs. anyhow fixes this and
more!
With the anyhow Result type, we can now add any Object as context around errors. To do so, we can use context or
withContext (lazily). Either will only have an effect if a Result is the Err subclass. In the following
example we will use Strings as the context, but using Exceptions, especially for the root cause is common practice
as well.
import 'package:anyhow/anyhow.dart';
void main() {
print(order("Bob", 1));
}
Result<String> order(String user, int orderNumber) {
final result = makeFood(orderNumber).context("Could not order for user: $user.");
if(result case Ok(:final ok)) {
return Ok("Order of $ok is complete for $user");
}
return result;
}
Result<String> makeFood(int orderNumber) {
if (orderNumber == 1) {
return makeHamburger().context("Order number $orderNumber failed.");
}
return Ok("pasta");
}
Result<String> makeHamburger() {
return bail("Hmm something went wrong making the hamburger.");
}
copied to clipboard
Output
Error: Could not order for user: Bob.
Caused by:
0: Order number 1 failed.
1: Hmm something went wrong making the hamburger.
StackTrace:
#0 AnyhowResultExtensions.context (package:anyhow/src/anyhow/anyhow_extensions.dart:12:29)
#1 order (package:anyhow/test/src/temp.dart:9:40)
#2 main (package:anyhow/example/main.dart:5:9)
... <OMITTED FOR EXAMPLE>
copied to clipboard
Now we know keep a record of exactly what was happening at each level in the call stack!
What Would This Look Like Without Anyhow
Before anyhow, if we wanted to accomplish something similar with Result, we had to do:
void main() {
print(order("Bob", 1));
}
Result<String, String> order(String user, int orderNumber) {
final result = makeFood(orderNumber);
if(result case Ok(:final ok)) {
return Ok("Order of $ok is complete for $user");
}
Logging.w("Could not order for user: $user.");
return result;
}
Result<String, String> makeFood(int orderNumber) {
if (orderNumber == 1) {
final result = makeHamburger();
if (result.isErr()) {
Logging.w("Order number $orderNumber failed.");
}
return result;
}
return Ok("pasta");
}
Result<String, String> makeHamburger() {
// What is the context around this error??
return Err("Hmm something went wrong making the hamburger.");
}
copied to clipboard
Which is more verbose/error-prone and may not be what we actually want. Since:
We may not want to log anything if the error state is
known and can be recovered from
Related logs should be kept together (in the example, other functions could log before this Result had been handled)
We have no way to get the correct stack traces related to the original issue
We have no way to inspect "context", while with anyhow we can iterate through with chain()
Now with anyhow, we are able to better understand and handle errors in an idiomatic way!
Configuration Options #
anyhow functionality can be changed by changing:
Error.hasStackTrace;
Error.displayFormat;
Error.stackTraceDisplayFormat;
Error.stackTraceDisplayModifier;
copied to clipboard
Which is usually done at startup.
hasStackTrace: With Error.hasStackTrace = false, we can exclude capturing a stack trace:
Error: Could not order for user: Bob.
Caused by:
0: Order number 1 failed.
1: Hmm something went wrong making the hamburger.
copied to clipboard
displayFormat: We can view the root cause first with Error.displayFormat = ErrDisplayFormat.rootCauseFirst
Root Cause: Hmm something went wrong making the hamburger.
Additional Context:
0: Order number 1 failed.
1: Could not order for user: Bob.
StackTrace:
#0 bail (package:anyhow/src/anyhow/functions.dart:6:14)
#1 makeHamburger (package:anyhow/test/src/temp.dart:31:10)
... <OMITTED FOR EXAMPLE>
copied to clipboard
stackTraceDisplayFormat: if we want to include none, the main, or all stacktraces in the output.
stackTraceDisplayModifier: Modifies the stacktrace during display. Useful for adjusting
number of frames to include during display/logging.
Base Result Type vs Anyhow Result Type #
The base Result type is re-exported from Result in rust_core. This is so anyhow could be standalone and
work seamlessly rust_core.
But most of the time you should just use the anyhow Result type.
The base Result Type and the anyhow Result Type can be imported with
import 'package:anyhow/base.dart' as base;
copied to clipboard
or
import 'package:anyhow/anyhow.dart' as anyhow;
copied to clipboard
Respectively. These types have parity (The anyhow Result type is just a typedef), thus can be used
together.
typedef Result<S> = base.Result<S, anyhow.Error>
copied to clipboard
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
}
copied to clipboard
If you don't want to import both libraries like above, and you need use both in the same file, you can just import the
anyhow one and use the Base prefix where necessary.
import 'package:anyhow/anyhow.dart';
void main(){
BaseResult<int,String> x = BaseErr("this is an error message");
BaseResult<int, Error> y = x.mapErr(anyhow); // or just toAnyhowResult()
Result<int> w = y; // just for explicitness in the example
assert(w.unwrapErr().downcast<String>().unwrap() == "this is an error message");
}
copied to clipboard
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.