搜尋套件、儲存庫與 APP 分類

套件詳情v3.0.1

anyhow

慣用的錯誤處理功能,讓您的程式碼更安全、更易維護,並更容易除錯。Dart 對 Rust 的 Result 單子類型與 "anyhow" 套件的實作。

AndroidiOSWebmacOSWindowsLinux
近 30 天下載
698
喜歡數
32
Pub 分數
150

安裝套件

flutter pub add anyhow

套件介紹

根據套件公開資料產生的選型評估,僅供參考。

anyhow 套件為 Dart 提供了直覺的錯誤處理機制,靈感來自 Rust 的 Result 單子與 'anyhow' 套件。它透過允許開發者以更具表現力和可組合的方式回傳與處理錯誤,簡化了錯誤傳播與管理。內建支援加入上下文與堆疊追蹤,提升除錯能力並改善程式碼可維護性。此套件設計目標是讓所有平台上的錯誤處理感覺自然且安全。

適用情境

  • 簡化非同步操作中的錯誤處理
  • 向錯誤中添加上下文資訊
  • 提高故障點的可調試性
  • 撰寫更安全且更易讀的程式碼
  • 統一處理多種錯誤類型

主要特色

  • 結果單子模式
  • 自動堆疊追蹤捕獲
  • 上下文錯誤包裝
  • 與 Dart 的 async/await 無縫整合
  • 最小化樣板程式碼

適合對象

  • 尋求類似 Rust 錯誤處理的開發者
  • 需要詳細錯誤診斷的專案
  • 重視程式碼可維護性的團隊
  • 具有複雜錯誤流程的應用程式
  • 跨平台 Flutter 應用

使用建議

  • 可能對初學者帶來學習曲線
  • 並非在所有情況下都能取代 try-catch
  • 需要理解函數式錯誤模式
  • 限制為非可空上下文
  • 最好與適當的日誌記錄結合使用

原始 README

以下為關聯原始碼儲存庫的 README 原文快照,最新內容請造訪原始碼儲存庫。

anyhow

Pub Version Dart Package Docs License: MIT Build Status

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 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.");
  if(result case Ok(v:final order)) {
    return Ok("Order of $order is complete for $user");
  }
  return result;
}

Result<String> makeFood(int orderNumber) {
  if (orderNumber == 1) {
    return makePizza().context("Order was number $orderNumber.");
  }
  return Ok("pasta");
}

Result<String> makePizza() {
  return bail("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:

void main() {
  print(order("Bob", 1));
}

Result<String, String> order(String user, int orderNumber) {
  final result = makeFood(orderNumber);
  if(result case Ok(v:final order)) {
    return Ok("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;
  }
  return Ok("pasta");
}

Result<String, String> makePizza() {
  // What is the context around this error??
  return Err("Pizza was missing a topping.");
}

Which is more verbose/error-prone and may not be what we actually want. Since:

  1. We may not want to log anything if the error state is known and can be recovered from
  2. Related logs should be kept together (in the example, other functions could log before this Result had been handled)
  3. We have no way to get the correct stack traces related to the original issue
  4. 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.displayOrder;
Error.stackTraceDisplayFormat;
Error.stackTraceDisplayModifier;

Which is usually done at startup.

  • 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';

void main(){
  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` package

void main(){
  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';

void main(){
  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';

void main(){
  Result<int> x = bail("this is an error message").context(1);
  final rootInner = x.unwrapErr().rootCause().downcastUnchecked();
  switch(rootInner) {
    case String():
      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.

所屬分類

依賴

依賴清單 1 項

原始碼儲存庫

使用此套件的專案

暫無相關資料。