패키지, 저장소 및 앱 카테고리 검색

패키지 상세v3.0.1

anyhow

코드를 더 안전하고 유지보수하기 쉬우며 오류 디버깅이 더 쉬워지도록 도와주는 자연스러운 오류 처리 기능. Rust의 Result 모나드 유형과 "anyhow" 크레이트의 Dart 구현.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
698
좋아요
32
Pub 점수
150

패키지 설치

flutter pub add anyhow

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

anyhow 패키지는 Rust의 Result 모나드와 'anyhow' 크레이트를 영감으로 하여 Dart에서 자연스러운 오류 처리를 제공합니다. 개발자가 오류를 더 표현적이고 조합 가능한 방식으로 반환하고 처리할 수 있도록 하여 오류 전파 및 관리를 간소화합니다. 내장된 컨텍스트 추가 및 스택 트레이스 지원을 통해 디버깅을 강화하고 코드 유지보수성을 향상시킵니다. 이 패키지는 모든 플랫폼에서 오류 처리가 자연스럽고 안전하게 느껴지도록 설계되었습니다.

사용 사례

  • 비동기 작업에서 오류 처리 간소화
  • 오류에 맥락 정보 추가하기
  • 실패 지점의 디버깅 가능성 향상
  • 더 안전하고 가독성 높은 코드 작성
  • 다양한 오류 유형을 일관되게 처리하기

주요 기능

  • 결과 모나드 패턴
  • 자동 스택 트레이스 캡처
  • 컨텍스트 오류 래핑
  • Dart의 async/await와의 원활한 통합
  • 최소한의 보일러플레이트

적합한 대상

  • Rust와 유사한 오류 처리를 원하는 개발자
  • 세부적인 오류 진단이 필요한 프로젝트
  • 코드 유지보수를 우선시하는 팀
  • 복잡한 오류 흐름을 가진 애플리케이션
  • 크로스 플랫폼 플러터 앱

고려 사항

  • 초보자에게 학습 곡선을 유도할 수 있음
  • 모든 경우에 try-catch의 대체가 되지 않는다
  • 기능적 오류 패턴을 이해해야 합니다
  • 널(null)이 아닌 컨텍스트에 한정됨
  • 적절한 로깅과 함께 사용하는 것이 가장 좋습니다

원본 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 개

소스 저장소

이 패키지를 사용하는 프로젝트

아직 데이터가 없습니다.