tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

【go】golangのエラー処理メモ - ①. errorとError型とカスタムErrorと

はじめに

goをさわって数ヶ月ですが、雰囲気では書けていたものの
errorやエラーハンドリングについてはもやもやしたままだったので自分理解メモの①

関連

この記事の関連です。

アジェンダ

  1. errorの基本の書き方
  2. Error型について
  3. 任意の文字列でError型を返す(errors.Newとfmt.Error)
  4. カスタムError

1. errorの基本の書き方

以下のコードがerrorについての基本です。

// Openに成功したらnil(エラー無し)が返される
// Openに失敗したらerror側の値が返却される
f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

返されるerror変数とnilを比較することで操作が成功したか判断します。
上記で言えば if err != nil { です。

os.Openの定義は以下のようになっています。

func Open(name string) (file *File, err error)
https://golang.org/pkg/os/#Open

2. Error型について

errorはgoのビルトインのインターフェース型の1つです。

type error interface {
    Error() string
}

多くの内部パッケージにおいて使用されるerrorは
errorsパッケージ以下で実装されたプライベート構造体errorStringらしいです。

// Package errors implements functions to manipulate errors.
package errors

// New returns an error that formats as the given text.
func New(text string) error {
  return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
  s string
}

func (e *errorString) Error() string {
  return e.s
}

使う場合は、errors.Newを通して文字列をerrorStringに変換し
インターフェースerrorを満たすオブジェクトを得る、という感じになってます。

3. 任意の文字列でError型を返す(errors.Newとfmt.Error)

Error型について触れましたが、任意の文字列でerror型を返すには主に以下の2つがメジャーです。

用途としての大きな違いは、
fmt.Errorはフォーマットを指定したエラーを返せる ので
固定文字列でよければerrors.New、可変文字列を含めたければfmt.Errorという感じでしょうか。

以下で使ってみます。

errors.New

errors.Newに文字列を渡すだけです。

サンプル

https://github.com/tweeeety/go-error/blob/master/src/go-error/sample01/main.go

package main

import (
  "errors"
  "fmt"
)

func errorsNewSsample() error {
  err := errors.New("this is errors.New sample.")
  return err
}

func main() {
  err := errorsNewSsample()

  fmt.Println(err)
  fmt.Printf("%T\n", err)
}
出力
$ go run src/go-error/sample01/main.go 
this is errors.New sample.
*errors.errorString

fmt.Error

フォーマット文字列を渡せるので、変数の値を一緒に出したい場合などに使います。

サンプル

https://github.com/tweeeety/go-error/blob/master/src/go-error/sample02/main.go

package main

import (
  "fmt"
)

func fmtErrorfSsample(str string) error {
  err := fmt.Errorf("this is fmt.Errorf sample. str: %s", str)
  return err
}

func main() {
  err := fmtErrorfSsample("hogehoge")
  fmt.Println(err)
  fmt.Printf("%T\n", err)
}
出力
$ go run src/go-error/sample02/main.go 
this is fmt.Errorf sample. str: hogehoge
*errors.errorString

4. カスタムError

2. Error型についてで触れた通り
errorインターフェース満たすにはError()を実装すれば良いです。

この方法で任意のカスタムError型を作ります。

sample01/main.go
package main

import (
  "fmt"
)

// カスタムErrorの構造体
type MyError struct {
  Msg  string
  Code int
}

// error interfaceを実装
func (err *MyError) Error() string {
  return fmt.Sprintf("ERROR: %d %s", err.Code, err.Msg)
}

// 何かする処理
func doSomething() error {
  return &MyError{Msg: "doSomething is unexpected error", Code: 30001}
}

func main() {
  if err := doSomething(); err != nil {
    fmt.Println(err)
    fmt.Printf("%T\n", err)
  }
}
出力
$ go run src/go-error/sample03/main.go
ERROR: 30001 doSomething is unexpected error
*main.MyError

おわり

errorの扱いについてサラっとまとめました。
あまり長いと書くのも読むのも疲れるのでエラーハンドリングについては別記事でかきます\(^o^)/

参考