0Pricing
Go Academy · レッスン

カスタムエラーの作成

errors.New、fmt.Errorf、センチネルエラー

「カスタムエラーの作成」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。

カスタムエラー型を使う理由

カスタムエラー型は、文字列メッセージ以外の構造化データを保持できます。これにより、呼び出し側は特定のエラー条件を調べ、それに応じて処理できます:

package main
import "fmt"

// Custom error type carries extra context
type HTTPError struct {
    StatusCode int
    Message    string
}

func (e *HTTPError) Error() string {
    return fmt.Sprintf("HTTP %d: %s", e.StatusCode, e.Message)
}

func fetch(url string) error {
    return &HTTPError{404, "page not found"}
}

func main() {
    err := fetch("http://example.com")
    fmt.Println(err) // HTTP 404: page not found
}

error インターフェースの実装

Error() string メソッドを持つ任意の型は、error を満たします:

package main
import "fmt"

type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: field %q value %v — %s", e.Field, e.Value, e.Message)
}

func validateAge(age int) error {
    if age < 0 || age > 150 {
        return &ValidationError{"age", age, "must be between 0 and 150"}
    }
    return nil
}

func main() {
    fmt.Println(validateAge(-5))
}

errors.New によるセンチネルエラー

センチネルエラーは、よく知られたエラー条件を表すパッケージレベルの変数です:

package main
import ("fmt"; "errors")

var (
    ErrNotFound      = errors.New("not found")
    ErrUnauthorized  = errors.New("unauthorized")
    ErrAlreadyExists = errors.New("already exists")
)

func getUser(id int) (string, error) {
    if id != 1 { return "", ErrNotFound }
    return "Alice", nil
}

func main() {
    _, err := getUser(99)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("user does not exist")
    }
}

ラップに %w を使う fmt.Errorf

%w を指定した fmt.Errorf はエラーをラップし、元のエラーを保持するため、errors.Is と errors.As で調べられます:

package main
import ("fmt"; "errors")

var ErrDatabase = errors.New("database error")

func queryDB() error {
    return fmt.Errorf("queryDB: %w", ErrDatabase)
}

func main() {
    err := queryDB()
    fmt.Println(err)                       // queryDB: database error
    fmt.Println(errors.Is(err, ErrDatabase)) // true — unwrapped
}

Unwrap を持つカスタムエラー

カスタムエラー型に Unwrap() error を実装すると、エラーチェーンに参加できるようになります:

package main
import ("fmt"; "errors")

var ErrNotFound = errors.New("not found")

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string {
    return fmt.Sprintf("query %q: %v", e.Query, e.Err)
}
func (e *QueryError) Unwrap() error { return e.Err }

func main() {
    err := &QueryError{"SELECT *", ErrNotFound}
    fmt.Println(err)
    fmt.Println(errors.Is(err, ErrNotFound)) // true
}

errors.Is — 同一性チェック

errors.Is はエラーチェーンをたどり、ターゲットと等しいエラーを探します:

package main
import ("fmt"; "errors")

var ErrTimeout = errors.New("timeout")

func deepCall() error {
    return fmt.Errorf("layer3: %w", fmt.Errorf("layer2: %w", ErrTimeout))
}

func main() {
    err := deepCall()
    fmt.Println(errors.Is(err, ErrTimeout)) // true — found deep in chain
}

errors.As — 型の取り出し

errors.As はエラーチェーン内でターゲット型に一致する最初のエラーを見つけ、それを代入します:

package main
import ("fmt"; "errors")

type PermissionError struct{ User string }
func (e *PermissionError) Error() string { return "permission denied for " + e.User }

func action(user string) error {
    return fmt.Errorf("action: %w", &PermissionError{user})
}

func main() {
    err := action("bob")
    var pe *PermissionError
    if errors.As(err, &pe) {
        fmt.Println("denied:", pe.User) // denied: bob
    }
}

複数エラーのラップ(Go 1.20 以降)

Go 1.20 で、エラーを結合する errors.Join と、複数の %w を指定できる fmt.Errorf が追加されました:

package main
import ("fmt"; "errors")

func validate(name, email string) error {
    var errs []error
    if name == ""  { errs = append(errs, errors.New("name required")) }
    if email == "" { errs = append(errs, errors.New("email required")) }
    return errors.Join(errs...)
}

func main() {
    err := validate("", "")
    fmt.Println(err)
    // name required
    // email required
}

エラー型の命名規則

Go におけるエラー変数と型の命名規則:

  • センチネルエラー変数:ErrXxx(例:ErrNotFound)
  • カスタムエラー型:XxxError(例:ValidationError)
  • センチネルエラーにはパッケージレベルの var を、詳細なエラーにはポインター型を使用します
  • 呼び出し側が errors.Is / errors.As で使用できるようにエクスポートします

それぞれの方法を使う場面

適切なエラー処理方法の選択:

  • errors.New — 追加データが不要で、センチネルの同一性をチェックする単純なエラー
  • fmt.Errorf — 必要に応じてラップできる、書式付きメッセージ
  • カスタム型 — 呼び出し側が構造化データ(フィールドやコード)を必要とする場合
  • errors.Join — 複数のバリデーションエラーをまとめる場合

確認問題

エラーチェーンから特定のエラー型を取り出せる関数はどれですか?

まとめ:カスタムエラー

まとめ:

  • Error() string を実装してカスタムエラー型を作成します
  • Unwrap() error を追加してエラーチェーンに参加させます
  • 同一性のチェックには errors.Is、型の取り出しには errors.As を使用します
  • センチネルエラーは ErrXxx、カスタム型は XxxError とします
  • errors.Join は複数のエラーをまとめます(Go 1.20 以降)

よくある質問

「カスタムエラーの作成」レッスンは無料ですか?

はい。「カスタムエラーの作成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。

「カスタムエラーの作成」で何を学びますか?

errors.New、fmt.Errorf、センチネルエラー ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Go Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「カスタムエラーの作成」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このGo Academyレッスンでコードを書いて実行できますか?

はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. errorインターフェース
  2. カスタムエラーの作成
  3. エラーのラップとアンラップ
  4. panic、recover、defer
← Go Academyに戻る