errors.Is によるアンラップ
CoddyのGOジャーニー「論理とフロー」セクションの一部 — レッスン 40/68。
ラップされたエラーを扱う際、エラーチェーンのどこかに特定のエラーが存在するかどうかを確認する方法が必要です。errors.Is 関数はまさにこの機能を提供し、センチネルエラーが複数回ラップされている場合でも、それを特定できるようにします。
errors.Is 関数は、調査したいエラーと、探しているターゲットエラーの2つのパラメータを受け取ります。ターゲットエラーがエラーチェーンのどこかで見つかった場合、true を返します:
var ErrNotFound = errors.New("not found")
wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)
if errors.Is(wrappedErr, ErrNotFound) {
fmt.Println("This is a not found error")
}これは、Goで特定のエラーをチェックするための現代的で推奨される方法です。文字列の比較や直接的な等価性のチェックとは異なり、errors.Is はラップされたエラーに対して正しく動作し、エラーチェーンを辿って一致するものを見つけます。これにより、エラーがアプリケーションの複数のレイヤーを通過する場合でも、エラーハンドリングが堅牢で信頼性の高いものになります。
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
チャレンジ
簡単ラップされたエラーチェーン内の特定の接続エラーを識別するために、errors.Isを使用するデータベース接続監視システムを構築します。このチャレンジでは、複数のアプリケーションレイヤーを通じて追加のコンテキストでラップされている場合でも、errors.Isを使用してセンチネルエラーを検出する方法を学びます。
2つの入力を受け取ります:
"database_name,operation,error_type"形式のデータベース操作の詳細を含む文字列(例:"users_db,connect,connection_timeout")"service_name,retry_count"形式のアプリケーションコンテキストを含む文字列(例:"AuthService,3")
あなたのタスクは以下の通りです:
errors.New()を使用して3つのセンチネルエラーを定義します:- メッセージが
"connection timeout"のErrConnectionTimeout - メッセージが
"connection refused"のErrConnectionRefused - メッセージが
"database not found"のErrDatabaseNotFound
- メッセージが
- カンマで分割して最初の入力を解析し、データベース名、操作、およびエラータイプを取得します
- カンマで分割して2番目の入力を解析し、サービス名とリトライ回数を取得します
- エラータイプに基づいて元のエラーを作成します:
- エラータイプが
"connection_timeout"の場合:ErrConnectionTimeoutを使用 - エラータイプが
"connection_refused"の場合:ErrConnectionRefusedを使用 - エラータイプが
"database_not_found"の場合:ErrDatabaseNotFoundを使用 - その他のエラータイプの場合:
errors.New("unknown database error")を使用
- エラータイプが
fmt.Errorfと%wを使用して、データベースレベルのラップされたエラーを作成します:"failed to [operation] database [database_name]: %w"- サービスレベルのラップされたエラーを作成します:
"[service_name] database operation failed after [retry_count] retries: %w" errors.Isを使用して、最終的なラップされたエラーが各センチネルエラーと一致するかどうかを確認し、結果を表示します:"Checking for connection timeout: [true/false]""Checking for connection refused: [true/false]""Checking for database not found: [true/false]"
- エラーチェーンを表示します:
"Error Chain:""Original: [original_error_message]""Database level: [database_level_error_message]""Service level: [service_level_error_message]"
- エラー分析を表示します:
"Error Analysis:""Database: [database_name]""Operation: [operation]""Service: [service_name]""Retries: [retry_count]""Error type detected: [detected_error_type]"(ここでdetected_error_typeは"Connection Timeout"、"Connection Refused"、"Database Not Found"、または"Unknown Error"のいずれか)
入力文字列をカンマで分割するために strings パッケージを、センチネルエラーと errors.Is のために errors パッケージを、そしてエラーのラップのために fmt パッケージを使用してください。このチャレンジは、errors.Is がラップされたエラーに対しても正しく機能する堅牢なエラー識別を提供し、複雑なアプリケーションにおける信頼性の高いエラーハンドリングに不可欠であることを示しています。
チートシート
errors.Is 関数は、エラーが複数回ラップされている場合でも、エラーチェーン内のどこかに特定のエラーが存在するかどうかを確認します:
var ErrNotFound = errors.New("not found")
wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)
if errors.Is(wrappedErr, ErrNotFound) {
fmt.Println("This is a not found error")
}errors.Is は、調査対象のエラーと検索対象のターゲットエラーの2つのパラメータを受け取ります。ターゲットエラーがエラーチェーン内のどこかで見つかった場合、true を返します。
これは Go で特定のエラーを確認するための現代的で推奨される方法です。文字列比較や直接的な等価性チェックとは異なり、ラップされたエラーに対しても正しく機能するためです。
自分で試してみよう
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
// 入力を読み込む
var dbOperation string
var appContext string
fmt.Scanln(&dbOperation)
fmt.Scanln(&appContext)
// 入力を解析する
dbParts := strings.Split(dbOperation, ",")
databaseName := dbParts[0]
operation := dbParts[1]
errorType := dbParts[2]
contextParts := strings.Split(appContext, ",")
serviceName := contextParts[0]
retryCount := contextParts[1]
// TODO: 以下にコードを記述してください
// 1. errors.New() を使用して3つのセンチネルエラーを定義する
// 2. エラータイプに基づいて元のエラーを作成する
// 3. fmt.Errorf と %w を使用してデータベースレベルのラップされたエラーを作成する
// 4. サービスレベルのラップされたエラーを作成する
// 5. errors.Is を使用して各センチネルエラーを確認する
// 6. 結果とエラーチェーンを表示する
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
論理とフローのすべてのレッスン
7エラーハンドリング詳説
カスタムエラー型%w によるエラーのラッピングerrors.Is によるアンラップerrors.As によるアンラップpanic の理解recover の使用まとめ:安全な除算