Go エラー処理
Go 言語は組み込みのエラーインターフェースを通じて、非常にシンプルなエラー処理メカニズムを提供します。
Go 言語のエラー処理は、従来の例外処理メカニズムではなく、エラーを明示的に返す方式を採用しています。この設計によりコードロジックがより明確になり、開発者がコンパイル時または実行時にエラーを明確に処理しやすくなります。
Go のエラー処理は、主に以下のメカニズムを中心に展開されます:
errorインターフェース:標準的なエラー表現。- 明示的な戻り値:関数の戻り値を通じてエラーを返します。
- カスタムエラー:標準ライブラリまたはカスタムの方法でエラーを作成できます。
panicとrecover:回復不能な重大なエラーを処理します。
error インターフェース
Go 標準ライブラリは、エラーの抽象を表す error インターフェースを定義しています。
error 型はインターフェース型であり、その定義は次のとおりです:
type error interface {
Error() string
}
- 実装
errorインターフェース:任意のError()メソッドを実装した型はすべてエラーとして使用できます。 Error()そのメソッドはエラーを説明する文字列を返します。
errors パッケージを使用したエラー作成
コード作成中に error インターフェース型を実装することで、エラー情報を生成できます。
簡単なエラーを作成:
実例
import (
"errors"
"fmt"
)
func main() {
err := errors.New("this is an error")
fmt.Println(err) // 出力:this is an error
}
関数は通常、最後の戻り値でエラー情報を返します。errors.New を使用するとエラー情報を返せます:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
次の例では、Sqrt を呼び出すときに負の数を渡すと、non-nil の error オブジェクトを取得します。このオブジェクトを nil と比較すると結果は true になるため、fmt.Println(fmt パッケージは error を処理するときに Error メソッドを呼び出します)が呼び出されてエラーが出力されます。以下の呼び出しのサンプルコードをご覧ください:
result, err:= Sqrt(-1)
if err != nil {
fmt.Println(err)
}
エラーの明示的な返却
Go では、エラーは通常関数の戻り値として返され、開発者は明示的にチェックして処理する必要があります。
エラーを明示的に返す:
実例
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
出力:
Error: division by zero
カスタムエラー
カスタム型を定義することで、error インターフェースを拡張できます。
カスタムエラー型:
実例
import (
"fmt"
)
type DivideError struct {
Dividend int
Divisor int
}
func (e *DivideError) Error() string {
return fmt.Sprintf("cannot divide %d by %d", e.Dividend, e.Divisor)
}
func divide(a, b int) (int, error) {
if b == 0 {
return 0, &DivideError{Dividend: a, Divisor: b}
}
return a / b, nil
}
func main() {
_, err := divide(10, 0)
if err != nil {
fmt.Println(err) // 出力:cannot divide 10 by 0
}
}
fmt パッケージとエラーフォーマット
fmtfmt パッケージはエラーのフォーマット出力をサポートしています:
%v:デフォルト形式。%+v:対応している場合は、詳細なエラー情報を表示します。%s:文字列として出力します。
実例
import (
"fmt"
)
// DivideError 構造体を定義
type DivideError struct {
dividee int
divider int
}
// `error` インターフェースを実装
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// `int` 型の除算を行う関数を定義
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常な場合
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 除数がゼロのときにエラー情報を返します
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
上記のプログラムを実行すると、出力結果は次のとおりです:
100/10 = 10
errorMsg is:
Cannot proceed, the divider is zero.
dividee: 100
divider: 0
errors.Is と errors.As の使用
Go 1.13 以降、errorserrors パッケージは以下を導入しました:errors.Isとerrors.Asエラーチェーンを処理するために使用されます:
errors.Is
特定のエラーであるか、そのエラーによってラップされたものであるかをチェックします。
実例
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func findItem(id int) error {
return fmt.Errorf("database error: %w", ErrNotFound)
}
func main() {
err := findItem(1)
if errors.Is(err, ErrNotFound) {
fmt.Println("Item not found")
} else {
fmt.Println("Other error:", err)
}
}
errors.As
エラーを特定の型に変換して、さらに処理できるようにします。
実例
import (
"errors"
"fmt"
)
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintf("Code: %d, Msg: %s", e.Code, e.Msg)
}
func getError() error {
return &MyError{Code: 404, Msg: "Not Found"}
}
func main() {
err := getError()
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Printf("Custom error - Code: %d, Msg: %s\n", myErr.Code, myErr.Msg)
}
}
panic と recover
Go の panic は回復不能なエラーを処理するために使用され、recover は panic から回復するために使用されます。
panic:
- プログラムをクラッシュさせ、スタック情報を出力します。
- プログラムが実行を継続できない場合によく使用されます。
recover:
- キャプチャ
panic、プログラムのクラッシュを防ぎます。
実例
import "fmt"
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("something went wrong")
}
func main() {
fmt.Println("Starting program...")
safeFunction()
fmt.Println("Program continued after panic")
}
上記のコードを実行すると、出力結果は次のとおりです:
Starting program... Recovered from panic: something went wrong Program continued after panicその他の拡張