サーチ…
前書き
Goでは、予期せぬ状況は例外ではなくエラーを使用して処理されます。このアプローチは、Javaや他のオブジェクト指向言語のtry / catchブロックよりも、errnoを使用するCのアプローチに似ています。ただし、エラーは整数ではなくインターフェイスです。
失敗する関数は、通常、最後の戻り値としてエラーを返します。このエラーがnilでなければ、何かが間違っていて、関数の呼び出し側はそれに応じてアクションを実行する必要があります。
備考
Goでエラーが発生しないことに注意してください。代わりに、失敗した場合にエラーを返します。
関数が失敗する可能性がある場合、最後に返される値は一般的にerror
型です。
// This method doesn't fail
func DoSomethingSafe() {
}
// This method can fail
func DoSomething() (error) {
}
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
}
エラー値の作成
エラーを作成する最も簡単な方法は、 errors
パッケージを使用することです。
errors.New("this is an error")
エラーに追加情報を追加する場合は、 fmt
パッケージも便利なエラー作成方法を提供します。
var f float64
fmt.Errorf("error with some additional information: %g", f)
ここでは関数からエラーが返される完全な例を示します:
package main
import (
"errors"
"fmt"
)
var ErrThreeNotFound = errors.New("error 3 is not found")
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns a specific error message
fmt.Println(DoSomething(3)) // returns an error variable
fmt.Println(DoSomething(4)) // returns a simple error message
}
func DoSomething(someID int) error {
switch someID {
case 3:
return ErrThreeNotFound
case 2:
return fmt.Errorf("this is an error with extra info: %d", someID)
case 1:
return nil
}
return errors.New("this is an error")
}
カスタムエラータイプを作成する
Goでは、エラーは文字列として記述できる値で表されます。ビルトインerror
インターフェイスを実装するすべてのタイプはエラーです。
// The error interface is represented by a single
// Error() method, that returns a string representation of the error
type error interface {
Error() string
}
次の例は、文字列複合リテラルを使用して新しいエラータイプを定義する方法を示しています。
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
カスタムエラータイプをエラーとして使用できるようになりました:
package main
import (
"fmt"
)
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns an error message
}
func DoSomething(someID int) error {
if someID != 1 {
return AuthorizationError("Action not allowed!")
}
// do something here
// return a nil error if the execution succeeded
return nil
}
エラーを返す
ゴーでは、エラーは発生しません。代わりに、失敗した場合にerror
を返し error
。
// This method can fail
func DoSomething() error {
// functionThatReportsOK is a side-effecting function that reports its
// state as a boolean. NOTE: this is not a good practice, so this example
// turns the boolean value into an error. Normally, you'd rewrite this
// function if it is under your control.
if ok := functionThatReportsOK(); !ok {
return errors.New("functionThatReportsSuccess returned a non-ok state")
}
// The method succeeded. You still have to return an error
// to properly obey to the method signature.
// But in this case you return a nil error.
return nil
}
メソッドが複数の値を返す場合(そして実行が失敗する可能性がある場合)、標準的な規則はエラーを最後の引数として返すことです。
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
s := "Success!"
// The method succeeded.
return s, nil
}
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
エラーの処理
Goでは、関数呼び出しからエラーを返すことができます。規約では、メソッドが失敗する可能性がある場合、最後に返された引数はerror
です。
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
// The method succeeded.
return "Success!", nil
}
複数の変数代入を使用して、メソッドが失敗したかどうかを確認します。
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
// This is executed only if the method didn't return an error
fmt.Println(result)
エラーに関心がない場合は、 _
割り当てて無視することができます。
result, _ := DoAndReturnSomething()
fmt.Println(result)
もちろん、エラーを無視することは深刻な意味を持つ可能性があります。したがって、これはお勧めできません。
複数のメソッド呼び出しがあり、チェーン内の1つ以上のメソッドがエラーを返す場合は、そのエラーを処理できる最初のレベルにエラーを伝播する必要があります。
func Foo() error {
return errors.New("I failed!")
}
func Bar() (string, error) {
err := Foo()
if err != nil {
return "", err
}
return "I succeeded", nil
}
func Baz() (string, string, error) {
res, err := Bar()
if err != nil {
return "", "", err
}
return "Foo", "Bar", nil
}
パニックから回復する
一般的な間違いは、スライスを宣言し、スライスを初期化せずにスライスからインデックスを要求し始めることです。これにより、「インデックスの範囲外の」パニックが発生します。次のコードは、プログラムを終了せずにパニックから回復する方法を説明しています。パニックの通常の動作です。ほとんどの状況で、パニックでプログラムを終了するのではなく、この方法でエラーを返すことは、開発またはテスト目的でのみ有効です。
type Foo struct {
Is []int
}
func main() {
fp := &Foo{}
if err := fp.Panic(); err != nil {
fmt.Printf("Error: %v", err)
}
fmt.Println("ok")
}
func (fp *Foo) Panic() (err error) {
defer PanicRecovery(&err)
fp.Is[0] = 5
return nil
}
func PanicRecovery(err *error) {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
//fmt.Println("Panicing")
//panic(r)
*err = r.(error)
} else {
*err = r.(error)
}
}
}
別の関数(クロージャではなく)を使用すると、同じ関数をパニックになりがちな他の関数で再利用することができます。