Idiomatische Fehlerbehandlungsfunktionen, um Ihren Code sicherer, wartbarer und fehlerfreundlicher zu gestalten. Dart-Implementierung des Result-Monaden-Typs und der "anyhow"-Bibliothek aus Rust.
Aus öffentlichen Paketdaten erstellte Orientierung.
Das anyhow-Paket bietet idiomatische Fehlerbehandlung in Dart, inspiriert durch Rusts Result-Monade und die 'anyhow'-Kiste. Es vereinfacht die Fehlerpropagation und -verwaltung, indem Entwickler Fehler auf eine ausdrucksstärkere und komponierbare Weise zurückgeben und behandeln können. Dank integrierter Unterstützung für Kontextzusatz und Stacktraces wird die Fehlersuche verbessert und die Wartbarkeit des Codes erhöht. Das Paket ist so gestaltet, dass die Fehlerbehandlung auf allen Plattformen natürlich und sicher erscheint.
Anwendungsfälle
Fehlerbehandlung bei asynchronen Operationen vereinfachen
Zusätzliche Kontextinformationen zu Fehlern hinzufügen
Verbesserung der Debugbarkeit von Fehlerpunkten
Sichereren und lesbareren Code schreiben
Mehrere Fehlerarten einheitlich behandeln
Hauptmerkmale
Result-Monade-Pattern
Automatische Stack-Trace-Aufzeichnung
Kontextuelle Fehlerumgebung
Nahtlose Integration mit Dart's async/await
Minimales Boilerplate
Geeignet für
Entwickler, die eine ähnliche Fehlerbehandlung wie Rust suchen
Projekte, die detaillierte Fehlerdiagnose erfordern
Teams, die der Code-Wartbarkeit Priorität einräumen
Anwendungen mit komplexen Fehlerflüssen
Plattformübergreifende Flutter-Anwendungen
Hinweise
Kann für Anfänger eine Lernkurve mit sich bringen
Kein Ersatz für try-catch in allen Fällen
Erfordert Verständnis funktionaler Fehlermuster
Beschränkt auf nicht-nullbare Kontexte
Am besten in Verbindung mit angemessener Protokollierung verwendet
Original-README
Originaler README-Schnappschuss aus dem verknüpften Quell-Repository. Aktuelle Inhalte finden Sie im Repository.
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.
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 anyhowResult 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';
voidmain() {
print(order("Bob", 1));
}
Result<String> order(String user, int orderNumber) {
final result = makeFood(orderNumber).context("Could not order for $user.");
if(result caseOk(v:final order)) {
returnOk("Order of $order is complete for $user");
}
return result;
}
Result<String> makeFood(int orderNumber) {
if (orderNumber == 1) {
returnmakePizza().context("Order was number $orderNumber.");
}
returnOk("pasta");
}
Result<String> makePizza() {
returnbail("Pizza was missing a topping.");
}
Output
Could not order for Bob.
Caused By:
0: Order was number 1.
1: Pizza was missing a topping..
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>
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:
voidmain() {
print(order("Bob", 1));
}
Result<String, String> order(String user, int orderNumber) {
final result = makeFood(orderNumber);
if(result caseOk(v:final order)) {
returnOk("Order of $order is complete for $user");
}
Logging.w("Could not order for $user.");
return result;
}
Result<String, String> makeFood(int orderNumber) {
if (orderNumber == 1) {
final result = makePizza();
if (result.isErr()) {
Logging.w("Order was number $orderNumber.");
}
return result;
}
returnOk("pasta");
}
Result<String, String> makePizza() {
// What is the context around this error??returnErr("Pizza was missing a topping.");
}
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!
hasStackTrace: With Error.hasStackTrace = false, we can exclude capturing a stack trace:
Could not order for Bob.
Caused By:
0: Order was number 1.
1: Pizza was missing a topping.
displayOrder: We can view the root cause first with Error.displayOrder = ErrorDisplayOrder.rootFirst
Pizza was missing a topping.
Additional Context:
0: Order was number 1.
1: Could not order for Bob.
StackTrace:
#0 bail (package:anyhow/src/anyhow/functions.dart:6:14)
#1 makePizza (package:anyhow/test/src/temp.dart:31:10)
... <OMITTED FOR EXAMPLE>
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.
Anyhow Result Type vs Rust Result Type
The Result type for this package is just a typedef of the Result type in the rust package.
Thus inheriting all capabilities and the two types/packages can be used seamlessly together -
import'package:anyhow/anyhow.dart'as anyhow;
import'package:rust/rust.dart';
voidmain(){
Result<int,anyhow.Error> x = Ok(1);
x.context(2);
anyhow.Result<int> y = Ok(1);
y.context(2);
assert(x == y);
Cell(1); // e.g. `rust` package type
}
or
import'package:anyhow/rust.dart'; // overlays anyhow on top of the `rust` packagevoidmain(){
Result<int> x = Ok(1);
x.context(1);
Cell(1); // e.g. `rust` package type
}
Downcasting
Downcasting is the process of getting the inner error from an an anyhow Error.
import'package:anyhow/anyhow.dart';
voidmain(){
Result<int> x = bail("this is an error message");
Error error = x.unwrapErr();
assert(error.downcast<String>().unwrap() == "this is an error message");
assert(error.downcastUnchecked() == "this is an error message"); // or
}
This may be useful when you want to inspect the root error type.
import'package:anyhow/anyhow.dart';
voidmain(){
Result<int> x = bail("this is an error message").context(1);
final rootInner = x.unwrapErr().rootCause().downcastUnchecked();
switch(rootInner) {
caseString():
print("String found");
default:
print("Default reached");
}
}
Since anyhow makes the trade off that you do not care about the underlying causes inner type,
which allows your api's to be more composable and concise,
downcasting is expected to be used sparingly.