go1.13のerrorのラップ

#Go1.13からエラーがラップできるようになった
Go1.13のバージョンからエラーのラップができるようになったみたいなので、自分なりにまとめてみました。

##新しく追加された関数
- func As(err error, target interface{}) bool
- func Is(err, target error) bool
- func New(text string) error
- func Unwrap(err error) error

## エラーのラップ方法
まず、エラーのラップ方法はこんな感じになります

```golang:wrapError.go
package main

import(
	"errors"
	"fmt"
)

var MyError = myError()

func myError() error { return errors.New("myError")}

func makeMyError() error {
	return MyError
}

func makeWrappedError() error {
	err := makeMyError()
	return fmt.Errorf("%w", err)
}

func main() {
	err := makeMyError()
	fmt.Printf("%T\n", err)

	wrappedErr := makeWrappedError()
	fmt.Printf("%T\n", wrappedErr)
}

```
このようにエラーをwrapするときには `fmt.Errorf("%w", err)` を使います。
実行結果は以下

```
*errors.errorString
*fmt.wrapError
```

きちんとwrapされているのがわかります。
今度はこのエラーを取り出すのにUnwrapします。

```golang:unwrap.go

func main() {
	wrappedErr := makeWrappedError()
	fmt.Printf("type:%T", wrappedErr)

	unWrappedErr := errors.Unwrap(wrappedErr)
	fmt.Printf("type:%T", unWrappedErr)

	nilErr := errors.Unwrap(unWrappedErr)
	fmt.Printf("type:%T", nilErr)
}
```

結果は以下。

```
type:*fmt.wrapError
type:*errors.errorString
type:<nil>
```
このようにUnwrapされて行き、wrapしていないエラーをUnwrapすると、nilを返すようになります。
ここまでがエラーのラップについてです。

##Is関数の使い方
エラーのラップはわかっていただけたと思いますが、これをどうやって使うの？という話になります。
まず、今までのエラーハンドリングについておさらいします。こんな感じ

```golang:errorHandle.go
func main() {
	err := makeMyError()
	if err != nil {
		switch err {
		case MyError:
			fmt.Printf("MyError: %s", err.Error())
		default:
			fmt.Printf("defaultError: %s", err.Error())
		}
	}
}

```
結果は以下

```
MyError: myError
```
これで、MyErrorを拾えています。ここでエラーをラップしたものに変えてみます。

```golang:wrapErrorHandle.go
func main() {
	err := makeWrappedMyError()
	if err != nil {
		switch err {
		case MyError:
			fmt.Printf("MyError: %s", err.Error())
		default:
			fmt.Printf("defaultError: %s", err.Error())
		}
	}
}

```

結果は

```
defaultError: myError
```
となってしまし、MyErrorとして拾えていません。ここで、Is関数の登場です。
Is関数を使うとこのようになります。

```golang:Is.go
func main() {
	err := makeWrappedError()
	if err != nil {
		if errors.Is(err, MyError) {
			fmt.Printf("MyError: %s", err.Error())
		} else {
			fmt.Printf("defaultError: %s", err.Error())
		}
	}
}
```

```golang:結果
MyError: myError
```
今度は、きちんとMyErrorとして拾えました。
このように、今までのエラーハンドリングでは扱えないパターンが出てくるので、Is関数を用いる必要が出てきます。

##As関数の使い方
As関数もIs関数と使う目的は同じですが、引数を見ればわかるように自分で定義したエラーとの比較などを行うときに用います。
このような独自のエラーがあるとしましょう。

```golang:myError.go
package main

import(
	"fmt"
)

type marshalError struct {
	msg string
	code int
}

func (e *marshalError)Error() string {
	return fmt.Sprintf("marsshal error :%s, code: %d", e.msg, e.code)
}

func marshal() error {
	return &marshalError{msg:"hoge", code:0}
}

func main() {
	err := marshal()
	if e, ok := err.(*marshalError); ok {
		fmt.Printf("%s", e.Error())
	} else {
		fmt.Printf("defaultError")
	}
}

```

```golang:結果
marsshal error :hoge, code: 0
```
これで、構造体をマーシャル（仮想で）して帰ってきたエラーがエラーがmainErrorであることを確かめられました。
しかし、このエラーがまたラップされていた場合拾えなくなってしまいます。

```golang:wrappedMyError.go
package main

import(
	"fmt"
)

type marshalError struct {
	msg string
	code int
}

func (e *marshalError)Error() string {
	return fmt.Sprintf("marsshal error :%s, code: %d", e.msg, e.code)
}

func marshal() error {
	return &marshalError{msg:"hoge", code:0}
}

func wrapError() error {
	err := marshal()
	return fmt.Errorf("%w", err)
}

func main() {
	err := wrapError()
	if e, ok := err.(*marshalError); ok {
		fmt.Printf("%s", e.Error())
	} else {
		fmt.Printf("defaultError")
	}
}
```

```golang:結果
defaultError
```

これを避けるためにAs関数を使います。

```golang:As.go
package main

import(
	"errors"
	"fmt"
)

type marshalError struct {
	msg string
	code int
}

func (e *marshalError)Error() string {
	return fmt.Sprintf("marsshal error :%s, code: %d", e.msg, e.code)
}

func marshal() error {
	return &marshalError{msg:"hoge", code:0}
}

func wrapError() error {
	err := marshal()
	return fmt.Errorf("%w", err)
}

func main() {
	err := wrapError()
	var targetErr *marshalError
	if errors.As(err, &targetErr) {
		fmt.Printf("%s", targetErr)
	} else {
		fmt.Printf("defaultError")
	}
}


```

```golang:結果
mainError: main error
```
これで、mainErrorを拾えました。

最後にこれを応用して、エラーの抽象度をあげて、上位の関数に返すようなコードを考えてみました。

```golang:Example.go
package main

import(
	"errors"
	"fmt"
)

type User struct {
	name string
	email string
}
type invalidStructError struct {
	original interface{} //元の構造体
	err error
	code int
}

func (e *invalidStructError)Error() string {
	return fmt.Sprintf("original: %v, %s", e.original, e.err.Error())
}

func marshal(v interface{}) error {
	err := &invalidStructError{original:v, err: errors.New("invalid Struct"), code: 0}
	if err != nil{
		return fmt.Errorf("marshal err: %w", err)
	}
	return nil
}

func main() {
	val := User{name: "hoge", email: "1"}
	err := marshal(val)
	var target *invalidStructError
	if errors.As(err, &target) {
		fmt.Printf("code: %d\n", target.code)
		fmt.Printf("%s\n", target.Error())
	} else {
		fmt.Printf("defaultError")
	}
}
```

```golang:結果
code: 0
original: {hoge 1}, invalid Struct
```


As関数を用いてtargetにerrをUnwrapしているので、codeのフィールドが扱えるようになりました。

##まとめ
このように、Go1.13ではエラーのラップができるようになりました。これにより、エラーをより上位の関数に渡すときに抽象化して渡すことができるようになります。
それに伴って、エラーのハンドリングが変わってくるので、新しい形式をうまく使いこなせると良さそうです。
まだまだgo初心者なので、間違いなどありましたらご指摘いただけると幸いです。

###参考サイト
https://cipepser.hatenablog.com/entry/go1.13-error-wrapping
https://golang.org/pkg/errors/#pkg-index