Go言語(プログラミング)入門メモ⑯

## カスタムエラーを作成する。
独自のエラーを作成する場合、Errorメソッドを作成してerrorというインターフェースを実装することでエラー内容の表示を変更することができる。
ただし、自分で作成したエラーを返り値として返す場合、ポインタとして返すことが推奨されている。

## errorインターフェースでカスタムエラーを作成する。
エラーを独自で作成するにはerrorインターフェースを利用する。
errorインターフェースは以下のように書かれている。
~~~
type error interface {
    Error() string
}
~~~
Errorメソッドをつくることで、fmt.Printlnでエラーの中身を表示したいときに自分なりのエラーを表示できる。
たとえば、自分で作ったmyFuncという関数がエラーを返したときに、エラーがnilでなければエラー内容を表示するという処理をするとする。

### ※復習（条件付き代入文）
条件付き代入文（conditional assignment）は、Go言語において特定の条件をチェックしながら同時に値を代入する構文です。主にエラーチェックに使用され、コードをより簡潔に書くことができます。

以下は、条件付き代入文の基本的な例です：

```go
if err := myFunc(); err != nil {
    fmt.Println(err)
}
```

この例では、`myFunc`の戻り値が`err`に代入され、もしその値が`nil`でない場合（つまりエラーが発生した場合）に`fmt.Println(err)`が実行されます。

### 詳細な解説

1. **代入部分 (`err := myFunc()`)**:
   - `myFunc`が実行され、その戻り値が`err`に代入されます。

2. **条件部分 (`err != nil`)**:
   - `err`が`nil`でないかどうかをチェックします。`nil`でない場合はエラーが発生したことを意味します。

3. **ブロック内のコード (`fmt.Println(err)`)**:
   - 条件が真の場合に実行されるコードです。ここではエラーメッセージを表示します。

このように、条件付き代入文を使うことで、値の代入と条件のチェックを一つのステートメントにまとめることができます。これにより、コードがより簡潔で読みやすくなります。
### 復習終わり



~~~
import (
    "fmt"
)

func myFunc() error {
    return nil
}

func main() {
    if err := myFunc(); err != nil {      //myFuncの返り値がnilでないか確認
        fmt.Println(err)　　　　　　　　　//エラー内容を表示
    }
}
~~~
myFunc関数で自分なりのエラーを返すようにする。
最初に、自分なりのエラーとしてstring型のUsernameを持つUserNotFoundという構造体を作成する。Usernameの値は、データベースの中にないユーザーの名前を想定している。
~~~
type UserNotFound struct {
    Username string
}
~~~
次にUserNotFoundにErrorメソッドを実装する。返り値は構造体の持つUsernameの値を返す。
~~~
func (e *UserNotFound) Error() string {
    return e.Username
}
~~~
続いて、myFuncの中で、何か問題が起きたときにnilではなくErrorを返すようにする。

変数okをfalseで初期化し、if文でokの値がtrueであればnilを返し、そうでなければ「&UserNotFound{Userbane:"mike"}」を返す。
~~~
func myFunc() error {
    // Something wrong(この部分で問題が起きたとする)
    ok := false
    if ok {
        return nil
    }
    return &UserNotFound{Username:"mike"}
}
~~~
myFunc関数を実行してエラー内容を表示すると、「mike」が表示される。
~~~
func main() {
    if err := myFunc(); err != nil {      
        fmt.Println(err)　　　　　　　　　//エラー内容を表示
    }
}

//以下のように表示される
mike
~~~
Errorメソッドの処理を変更してみる。返り値を「fmt.Sprintf("User not found: %v", e.Username)」とSprintfで文字列と組み合わせて表示する。実行すると「User not found: mike」と自分なりのエラーを表示できる。
~~~
func (e *UserNotFound) Error() string {
    return fmt.Sprintf("User not found: %v", e.Username)
}

//以下のように出力される
User not found: mike
~~~

## エラーをポインタで返す
Errorメソッドにて、＊UserNotFoundに＊が使われている。しかし、構造体の中身は変更していないので、＊の必要性がないかもしれない。実際に＊を取ってコードを実行してみる。(myFunc関数の返り値の&も取る)
すると、さきほどと同様の出力結果にはなる。
~~~
func (e UserNotFound) Error() string {
    return fmt.Sprintf("User not found: %v", e.Username)
}

func myFunc() error {
    // Something wrong(この部分で問題が起きたとする)
    ok := false
    if ok {
        return nil
    }
    return UserNotFound{Username:"mike"}
}

//以下のように出力される
User not found: mike
~~~
しかし、Errorは&と＊をつけてポイントレシーバとすることが推奨されている。
これは、エラー内容を比較する際に問題が起きる可能性があるため。

## 発生したエラーの比較
&と＊を書いて実装する。
~~~
func (e *UserNotFound) Error() string {
    return fmt.Sprintf("User not found: %v", e.Username)
}

func myFunc() error {
    // Something wrong(この部分で問題が起きたとする)
    ok := false
    if ok {
        return nil
    }
    return &UserNotFound{Username:"mike"}
}
~~~
main関数内で変数e1,e2に対して、それぞれUserNotFoundを初期化して代入する。
「fmt.Println(e1 == e2)」を実行すると、trueが表示された。
~~~
func main() {
    e1 := UserNotFound{"mike"}
    e2 := UserNotFound{"mike"}
    fmt.Println(e1 == e2)
}

//以下のように出力される
true
~~~
一方でそれぞれのエラーに&を書いて実行すると、falseが表示される。
~~~
func main() {
    e1 := &UserNotFound{"mike"}
    e2 := &UserNotFound{"mike"}
    fmt.Println(e1 == e2)
}

//以下のように出力される
false
~~~
コードの異なる箇所でエラーが発生した場合に、ポインタで比較した場合は異なるエラーとして判定される。これを活用して、たとえば、次のように一つ目と二つ目のエラーがそれぞれ別の処理をしたい場合、エラーハンドリングを変化させることがある。
~~~
func main() {
    e1 := &UserNotFound{"mike"}
    e2 := &UserNotFound{"mike"}
    fmt.Println(e1 == e2)
    if err := myFunc(); err != nil {
        fmt.Println(err)
        if err = e1 {  //e1の時の処理を書く

        }
        if err = e2 {  //e2の時の処理を書く

        }
    }
}
~~~
## 学習に使用した教材
・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897