Go 触ってみたけど、違和感多め。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680230/c0bb518d-1141-c01d-635d-d43f23f08a31.png)

# はじめに
仕事で Go のコードを追いかける事があったので、ちょっと Go に興味が湧き触ってみました。
ページ下部の"参考"に記載されている Microsoft のサイトでハンズオン。ポイントと感想を記載します

# 開発環境
- OS : Windows 11 Pro
- go : 1.23.3 windows/amd64
- Cursor(IDE) : 0.42.3

# Go とは？
- [こちら](https://learn.microsoft.com/ja-jp/training/modules/go-get-started/1-what-is-go)に解りやすくまとまってます
- ざっくり言うと
    - シンプルで効率的
        - Go は直感的な構文と高速なコンパイル速度を特徴としており、学習しやすく使いやすい設計
    - 高い並行性
        - Go のゴルーチンとチャネルにより、並列処理を簡潔かつ効率的に実現できる
    - 幅広い用途
        - サーバーサイド開発、クラウドアプリケーション、システムプログラムなど、多様な領域で活用できる

# インストール

## Go ワークスペースを構成する
- ワークスペースを構成するには、ローカルの Go 環境変数で、Go プロジェクト フォルダーの場所を指定します
    ```
    > [Environment]::SetEnvironmentVariable("GOPATH", "<project-folder>", "User")
    ```
- ただし、Go 1.13 以降は、下記になります
    - **反映には、プロンプトの再起動が必要！**　20 分ぐらい悩みました
    ```
    go env -w GOPATH=<project-folder>
    ```

## Go ワークスペース フォルダー
- Go ワークスペースには、次の 3 つの基本フォルダーがあります
    - bin
        - アプリケーションの実行可能ファイルを含めます
    - src
        - ワークステーションに存在するすべてのアプリケーションのソース コードを含めます
    - pkg
        - 使用可能なライブラリのコンパイル済みバージョンを含めます。 再コンパイルせずに、コンパイラから、これらのライブラリにリンクできます

**スゴク違和感。** 慣れなんですかね

## サンドボックス環境
- [Go Playground](https://go.dev/play/) が用意されていて、サクッと触れるのは素晴らしいです


# 変数、関数
## rune 
- int32 ではダメなんですか？

## uint 
- まぁ、たまに必要になるかな

## 使用しない変数
- 使用しない変数を宣言すると Go のエラーが出る。IDE で波線出てても、たまに消し忘れるからいいですね。ただ、逆に関数の戻り値とかで、利用しない変数は `_` で表現する手間はありますね

## 関数
- 戻り値に名前を設定すると、`return` に何も記載しないのは見難い（断捨離しすぎ）
    ```
    func sum(number1 string, number2 string) (result int) {
        int1, _ := strconv.Atoi(number1)
        int2, _ := strconv.Atoi(number2)
        result = int1 + int2
        return
    }
    ```

- 複数の値を返すのが気持ち悪い
    ```
    func calc(number1 string, number2 string) (sum int, mul int) {
        int1, _ := strconv.Atoi(number1)
        int2, _ := strconv.Atoi(number2)
        sum = int1 + int2
        mul = int1 * int2
        return
    }
    ```
    ```
    func main() {
        sum, mul := calc(os.Args[1], os.Args[2])
        fmt.Println("Sum:", sum)
        fmt.Println("Mul:", mul)
    }
    ```
- 関数のパラメーター値 (ポインター)は慣れるまで注意が必要（パット見で見逃す）
    ```
    func main() {
        firstName := "John"
        updateName(&firstName)
        fmt.Println(firstName)
    }
    
    func updateName(name *string) {
        *name = "David"
    }
    ```

# パッケージ

## パッケージ名とファイル名
- Java のファイル名とクラス名と同じようにファイル名とパッケージ名は別でも OK
    - ただし、別にすると"モジュール参照"で分かり難くなる

## モジュール（パッケージ）
- Go のパッケージは、Java のパッケージとは違う。Java のクラス名みたいなもの
**下記の参照から`go_helloworld`と`go_calculator`の関係を良く見てください！**

## 外部パッケージを参照（パッケージの公開）
- モジュール作成にはコマンドが必要
    - `> go mod init github.com/[モジュールパッケージ]`
    - "go.mod" ファイルが生成される
- GitHub で公開。コード上に `github.com/XXX` とベタで記載されてるのは変な感じ

# 制御フロー

## fallthrough
- switch では `break` を記載しない。1 case で switch ブロックが終了になるのは、解り易くて良いと思う
    - 他の言語と同様にフォールスルーするには `fallthrough` を case の最後に記載
## panic / recover
- java のtry/catch な感じだけど、書く場所には慣れが必要。特に `recover` は感覚とは違う
    ```
    func highlow(high int, low int) {
    	if high < low {
    		fmt.Println("Panic!")
    		panic("highlow() low greater than high")
    	}
    	defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
    	fmt.Println("Call: highlow(", high, ",", low, ")")
    
    	highlow(high, low+1)
    }
    
    func main() {
    	defer func() {
    		handler := recover()
    		if handler != nil {
    			fmt.Println("main(): recover", handler)
    		}
    	}()
    
    	highlow(2, 0)
    	fmt.Println("Program finished successfully!")
    }
    ```

# データ型

## 配列、スライス、マップ
- `make` で初期化。`new` と似ているけど別物

# ログ

## ログのファイルパス設定
    ```
    log.SetOutput(file)
    ```

## log 以外のログ記録フレームワーク 
- Logrus、zerolog、zap、Apex

# メソッド／インターフェース

## メソッド
- Java より柔軟だけど、"レシーバー"に違和感
- メソッドで変数を更新する場合は、ポインターを利用する
    ```
    type triangle struct {
        size int
    }
    
    func (t triangle) perimeter() int {
        return t.size * 3
    }
    func (t *triangle) doubleSize() {
        t.size *= 2
    }
    ```

## インターフェイス
- インターフェースと実装に関連がない
    ```
    type Shape interface {
        Perimeter() float64
        Area() float64
    }
    type Square struct {
        size float64
    }
    
    func (s Square) Area() float64 {
        return s.size * s.size
    }
    
    func (s Square) Perimeter() float64 {
        return s.size * 4
    }
    func main() {
        var s Shape = Square{3}　←ここインターフェースと実装がマッチ
        fmt.Printf("%T\n", s)
        fmt.Println("Area: ", s.Area())
        fmt.Println("Perimeter:", s.Perimeter())
    }
    ```

# コンカレンシー
Go には、同時実行プログラムを作成するためのスタイルが 2 つある。
1 つは、他の言語と同様に、スレッドを使用する従来のスタイル。もう 1 つは、goroutines という独立したアクティビティ間で値が渡される Go のスタイル

ざっくり言うと、go で平行処理する時には "goroutine"（スレッドみたいな） を利用する。

## goroutine （ゴールーチン）
    ```
    func main() {
    	start := time.Now()
    
    	apis := []string{
    		"https://management.azure.com",
    		"https://dev.azure.com",
    		"https://api.github.com",
    		"https://outlook.office.com/",
    		"https://api.somewhereintheinternet.com/",
    		"https://graph.microsoft.com",
    	}
    
    	for _, api := range apis {
    		go checkAPI(api) // ここが同時実行される！
    	}
    
    	elapsed := time.Since(start)
    	fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
    }
    
    func checkAPI(api string) {
    	_, err := http.Get(api)
    	if err != nil {
    		fmt.Printf("ERROR: %s is down!\n", api)
    		return
    	}
    
    	fmt.Printf("SUCCESS: %s is up and running!\n", api)
    }
    ```

### goroutine の特徴
- 処理順序が保証されない
- メインゴルーチン（main）が終わると他のゴルーチンの終了を待たずにプログラム全体が終了する

## チャネル
goroutine 間で値を送信する必要があるとき、チャネルを使用する。

ざっくり言うと、異なるスレッド（goroutine）同士で、値をやりとり（送受信）すること。
なんとなく、別空間で流しっぱでない（戻りを待つ）場合にチャンネルを使うイメージしてます

### チャンネルのいろいろ
- バッファ有り／無し
    - バッファーありのチャネルの場合、プログラムがブロックされずにデータが送受信され、バッファーありのチャネルはキューのように動作する
- チャネルの方向
    - パラメータで指定したチャンネルへの方向（送信／受信）と異なる方向にするとコンパイルエラーになる
- 多重化
    - 複数チャンネルの制御。イベント発生での待機など

### チャンネルの理解
言葉だけだと理解できないので、ページ下部の GitHub から `go_goroutine` リポジトリをご覧ください。

# テスト（ユニットテスト）
Java の Junit とほぼ同じ

## 名称

- ファイル名 : XXX_test.go
- 関数名 : TestXXX(t *testing.T)

## インポート

- import "testing"

## サンプルアプリ
銀行業務をイメージしたサンプルアプリ。ページ下部の GitHub から `go_bankcore`, `go_bankapi` リポジトリをご覧ください。


# おわりに
まだ、1 歩も進めてないけど、なんとなく切り分けは出来るようになりました。
Go は違和感多めですが、手軽に始められる言語で好感を持てました。特に goroutine とチャンネルを使った平行処理は他のサービスなどと連携する際には使いどころが多いと思います。

ダラダラと感想を書いてしまったので、次回の投稿はもっとポイントを絞って見やすい記事にしたいです。

---
**参考（感謝）**
- [Goの初心者が見ると幸せになれる場所](https://qiita.com/tenntenn/items/0e33a4959250d1a55045)
- [プログラミング言語Go完全入門](https://docs.google.com/presentation/d/1RVx8oeIMAWxbB7ZP2IcgZXnbZokjCmTUca-AbIpORGk/edit#slide=id.g4f417182ce_0_0)
- [Microsoft Go の最初の手順](https://learn.microsoft.com/ja-jp/training/paths/go-first-steps/)
- [GitHub go_helloworld](https://github.com/mt-magic/go_helloworld)
- [GitHub go_calculator](https://github.com/mt-magic/go_calculator)
- [GitHub go_goroutine ](https://github.com/mt-magic/go_goroutine)
- [GitHub go_bankcore](https://github.com/mt-magic/go_bankcore)
- [GitHub go_bankapi](https://github.com/mt-magic/go_bankapi)