Go

【Go】エラーチェーン

前回はエラーのラッピングについて確認しました。

今回はカスタムエラーを作成して、そのカスタムエラーがエラーを保持(ラッピング)するようなエラーチェーンについて確認します。

やること

エラーを保持するカスタムエラー

3つのカスタムエラー(TopError、MidError、LowError)を作成します。
例えばTopの場合は以下のようになります。

type TopError struct {
	err error
}

func (t TopError) Error() string {
	return "TopError!!"
}

エラーを返すインスタンス

3つの構造体(Top、Mid、Low)を定義してdoメソッドを作成します。doメソッドは下層のエラーを保持してエラー返します。
Topの場合は以下のようになります。

type Top struct {
}

// Midのエラーを保持してエラーを返す
func (t *Top) do() error {
	m := Mid{}
	return TopError{err: m.do()}
}

階層と関係

  1. main が 「Top.do()メソッド実行」
  2. Top.do()メソッド が 「Mid.do()メソッド実行」
  3. Mid.do()メソッド が 「Low.do()メソッド実行」

各々の階層で下層のエラーを保持してエラー返します。

各層の実装

最下層 – Low.go

package main

type Low struct {
}

func (l *Low) do() error {
	return LowError{}
}

type LowError struct {
}

func (l LowError) Error() string {
	return "LowError!!"
}

中層 – Mid.go

package main

type Mid struct {
}

// Lowのエラーを保持してエラーを返す
func (m *Mid) do() error {
	l := Low{}
	return MidError{err: l.do()}
}

type MidError struct {
	err error
}

func (m MidError) Error() string {
	return "MidError!!"
}

func (m MidError) Unwrap() error { // 重要
	return m.err
}

最上層 – Top.go

package main

type Top struct {
}

// Midのエラーを保持してエラーを返す
func (t *Top) do() error {
	m := Mid{}
	return TopError{err: m.do()}
}

type TopError struct {
	err error
}

func (t TopError) Error() string {
	return "TopError!!"
}

func (t TopError) Unwrap() error { // 重要
	return t.err
}

メイン

各々の層のエラーがあるかを判定します。

package main

import (
	"errors"
	"fmt"
)

func main() {

	t := Top{}

	// 最下層のエラーを取得する
	if errors.As(t.do(), &TopError{}) {
		fmt.Println("Topのエラーがあった!!")
	} else {
		fmt.Println("Topのエラーはない!!")
	}

	// 中間層のエラーを取得する
	if errors.As(t.do(), &MidError{}) {
		fmt.Println("Midのエラーがあった!!")
	} else {
		fmt.Println("Midのエラーはない!!")
	}

	// 最上層のエラーを取得する
	if errors.As(t.do(), &LowError{}) {
		fmt.Println("Lowのエラーがあった!!")
	} else {
		fmt.Println("Lowのエラーはない!!")
	}
}

結果とポイント

結果

ちゃんと判定してくれました。

ポイント

Unwrap関数を実装しないとerrors.As関数は判定してくれません。errors.As関数は第一引数のエラーのUnwrap関数を実行し、第二引数(targetと呼称する)のエラーと比較します。一致しない場合、同じ事を繰り返します。なので、各階層でUnwrap関数を実装する事でerrors.As関数はtargetのエラーと同じかどうか判定し続けます。

コメントを残す

メールアドレスが公開されることはありません。

© DeNnie.Lab All Rights Reserved.