センチネルエラー
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 55/107。
センチネルエラーは、特定のエラー条件を表す、事前に定義されたパッケージレベルのエラー変数です。errors.New を使ってその都度新しいエラーを作成するのとは異なり、センチネルエラーは一度だけ宣言され、コード全体で再利用されます。
センチネルエラーはパッケージレベルの変数として定義します。通常、名前は Err で始まります。
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)特定の条件が発生すると、関数は以下の定義済みエラーを返します:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}センチネルエラーの強力な点は、呼び出し側がエラー値を直接比較することで、特定のエラー条件を確認できることです。
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}Goの標準ライブラリは、センチネルエラーを広範囲に使用しています。例えば、io.EOFは入力の終了を知らせ、sql.ErrNoRowsはクエリ結果が空であることを示します。このパターンは、パッケージとそのユーザーの間で、どのようなエラーが期待されるかについて明確な規約を作成します。
しかし、エラーがラップされている場合、単純な等価性チェックは機能しなくなります。次のレッスンでは、errors.Is() がエラーチェーン全体をチェックすることで、どのようにこの問題を解決するかを学びます。
チャレンジ
簡単特定の失敗条件を伝えるためにセンチネルエラーを使用する在庫管理システムを構築しましょう。呼び出し側がさまざまなシナリオを適切に処理できるように、パッケージレベルのエラー変数を定義します。
コードを2つのファイルに分けて構成します:
inventory.go: 一般的な失敗ケースに対して事前定義されたセンチネルエラーを持つ在庫システムを作成します。パッケージレベルで3つのセンチネルエラーを定義します:
ErrItemNotFoundメッセージ:"item not found in inventory"ErrOutOfStockメッセージ:"item is out of stock"ErrInsufficientQuantityメッセージ:"insufficient quantity available"
アイテム名とその数量のマップを保持する
Inventory構造体を作成します。以下のメソッドを実装してください:NewInventory() *Inventory- 空の在庫を作成しますAddItem(name string, quantity int)- アイテムの数量を追加または更新しますGetQuantity(name string) (int, error)- アイテムの数量を返します。存在しない場合はErrItemNotFoundを返しますPurchase(name string, quantity int) error- アイテムの購入を試みます。アイテムが存在しない場合はErrItemNotFound、現在の数量がゼロの場合はErrOutOfStock、要求された数量が在庫を超えている場合はErrInsufficientQuantityを返します。成功した場合は、在庫を減らしてnilを返します。
main.go: 入力から在庫操作を読み取り、呼び出し側が直接の等価比較を使用して特定のセンチネルエラーをチェックする方法を示します。入力からアイテム名と購入数量を読み取ります。在庫を作成し、アイテム
"laptop"を数量5で追加してから、購入を試みます。返されたエラーに基づいて、どのセンチネルエラーが発生したかを特定したことを示す特定のメッセージを出力します。
以下の入力が提供されます:
- 1行目:購入するアイテム名
- 2行目:購入する数量
どの特定のエラーが返されたかをチェックして、結果を処理します:
- もし
err == ErrItemNotFoundなら:Error: Item '[name]' does not exist in our inventoryと出力します - もし
err == ErrOutOfStockなら:Error: Item '[name]' is currently out of stockと出力します - もし
err == ErrInsufficientQuantityなら:Error: Cannot purchase [quantity] units of '[name]' - not enough in stockと出力します - 成功した場合:
Successfully purchased [quantity] units of '[name]'と出力します
例えば、 laptop と 3 が与えられた場合、出力は以下のようになります:
Successfully purchased 3 units of 'laptop'また、 phone と 2 が与えられた場合、出力は以下のようになります:
Error: Item 'phone' does not exist in our inventoryまた、 laptop と 10 が与えられた場合、出力は以下のようになります:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockチートシート
センチネルエラーとは、特定のエラー条件を表す、事前に定義されたパッケージレベルのエラー変数です。センチネルエラーは一度宣言され、コード全体で再利用されます。
センチネルエラーはパッケージレベルの変数として定義し、通常は名前を Err で始めます:
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)関数は、特定の条件が発生したときに、これらの定義済みエラーを返します:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}呼び出し側は、エラー値を直接比較することで、特定のエラー条件をチェックできます:
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}Goの標準ライブラリでは、io.EOF や sql.ErrNoRows など、センチネルエラーが広く使用されています。
自分で試してみよう
package main
import (
"fmt"
)
func main() {
// 入力を読み込む
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// インベントリを作成し、数量5で "laptop" を追加する
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: アイテムの購入を試みる
// inv.Purchase(itemName, quantity) を呼び出す
// TODO: 直接比較を使用して、どの特定のセンチネルエラーが返されたかを確認する
// if/else if 文を使用して以下を確認する:
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (成功)
// TODO: エラーに基づいて適切なメッセージを出力する
// ErrItemNotFound の場合: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// ErrOutOfStock の場合: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// ErrInsufficientQuantity の場合: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// 成功の場合: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー