errors.Is() と errors.As()
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 56/107。
エラーがラップされると、err == ErrNotFound のような単純な等価性のチェックは機能しなくなります。errors パッケージは、ラップされたエラーチェーンを検査するための2つの関数、errors.Is() と errors.As() を提供しています。
errors.Is() は、エラーチェーン内のいずれかのエラーが特定のセンチネルエラーと一致するかどうかを確認します:
import "errors"
var ErrNotFound = errors.New("not found")
func FindUser(id int) error {
return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}
err := FindUser(42)
if errors.Is(err, ErrNotFound) {
fmt.Println("User doesn't exist")
}返されたエラーがラップされていても、errors.Is() はチェーンをアンラップして、内部にある ErrNotFound を見つけます。
errors.As() は、エラーチェーン内のいずれかのエラーが特定の*型*に一致するかどうかを確認し、それを抽出します:
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("Invalid field:", ve.Field)
}センチネルエラーをチェックする場合は errors.Is() を使用し、カスタムエラー型のフィールドにアクセスする必要がある場合は errors.As() を使用します。どちらの関数もエラーチェーン全体を走査するため、ラップされたエラーを適切に処理するために不可欠です。
チャレンジ
簡単errors.Is() と errors.As() を使用して、ラップされたエラーチェーンを適切に検査する方法を示すファイル処理システムを構築しましょう。異なるレイヤーを通過する際にラップされるエラーを作成し、これらの関数を使用してエラーを特定し、そこから情報を抽出します。
コードは2つのファイルに分けて構成します:
errors.go: ファイルシステムの型エラーインフラストラクチャを定義します。センチネルエラーを作成します:
ErrPermissionDenied、メッセージは"permission denied"
カスタムエラー型を作成します:
FileError構造体、Filename(string) とOperation(string) フィールドを持つ- その
Error()メソッドは[Operation] failed on file: [Filename]を返す必要があります
ファイル操作をシミュレートし、エラーをラップする2つの関数を実装します:
ReadFile(filename string) error- filename が"secret.txt"の場合、ErrPermissionDeniedを返します。filename が"missing.txt"の場合、Operation が"read"で指定された Filename を持つ*FileErrorを返します。それ以外の場合はnilを返します。ProcessFile(filename string) error-ReadFileを呼び出します。エラーが返された場合、fmt.Errorfを使用して"processing failed: %w"という形式でラップします。それ以外の場合はnilを返します。
main.go: 入力からファイル名を読み取り、ProcessFileを呼び出します。errors.Is()とerrors.As()を使用してラップされたエラーチェーンを検査し、内部で見つかった内容に基づいて適切なメッセージを出力します。
以下の入力が提供されます:
- 1行目:処理するファイル名
エラーチェーンを検査して結果を処理します:
errors.Is()がチェーン内でErrPermissionDeniedを見つけた場合:完全なエラーメッセージを出力し、次の行にAccess denied - check file permissionsと出力します。errors.As()がチェーン内で*FileErrorを見つけた場合:完全なエラーメッセージを出力し、次の行にFile issue: [Filename] during [Operation]と出力します。- エラーが発生しなかった場合:
File '[filename]' processed successfullyと出力します。
例えば、secret.txt が与えられた場合、出力は以下のようになります:
processing failed: permission denied
Access denied - check file permissionsまた、missing.txt が与えられた場合、出力は以下のようになります:
processing failed: read failed on file: missing.txt
File issue: missing.txt during readそして、data.txt が与えられた場合、出力は以下のようになります:
File 'data.txt' processed successfullyエラーが ProcessFile によってラップされているにもかかわらず、errors.Is() で元のセンチネルエラーを検出し、errors.As() でカスタムエラー型を抽出できることに注目してください。これらの関数は、エラーチェーン全体をトラバースして、探しているものを見つけ出します。
チートシート
エラーがラップされている場合は、errorsパッケージのerrors.Is()とerrors.As()を使用してエラーチェーンを検査します。
errors.Is()は、チェーン内のいずれかのエラーがセンチネルエラーと一致するかどうかを確認します:
import "errors"
var ErrNotFound = errors.New("not found")
err := fmt.Errorf("database lookup failed: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
// ErrNotFoundがチェーン内で見つかりました
}errors.As()は、チェーン内のいずれかのエラーが特定の型と一致するかどうかを確認し、それを抽出します:
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
// veにチェーン内のValidationErrorが含まれるようになりました
fmt.Println(ve.Field)
}センチネルエラーにはerrors.Is()を使用し、カスタムエラー型のフィールドにアクセスする必要がある場合にはerrors.As()を使用します。どちらの関数もエラーチェーン全体を走査します。
自分で試してみよう
package main
import (
"errors"
"fmt"
)
func main() {
// 入力からファイル名を読み込む
var filename string
fmt.Scanln(&filename)
// ファイル名を使用してProcessFileを呼び出す
err := ProcessFile(filename)
// TODO: エラーチェーンを検査して結果を処理する
//
// errors.Is() を使用して、ErrPermissionDenied がチェーン内にあるか確認する:
// - 見つかった場合:エラーの全文を出力し、次に "Access denied - check file permissions" を出力する
//
// errors.As() を使用して、*FileError がチェーン内にあるか確認する:
// - 見つかった場合:エラーの全文を出力し、次に "File issue: [Filename] during [Operation]" を出力する
//
// エラーがない場合:"File '[filename]' processed successfully" を出力する
_ = errors.Is // ヒント: errors.Is() を使用する
_ = errors.As // ヒント: errors.As() を使用する
_ = err
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー