これから始めるなら気にしたいGoの作法

この記事は [Goのカレンダー | Advent Calendar 2021 - Qiita](https://qiita.com/advent-calendar/2021/go) の3つ目のカレンダーの3日目です。

# これはなに？
- あくまで筆者の主観で、Golangを使ったアプリケーションはこういった書き方が多い/こういう風にすると良さそう、を書き連ねる記事
    - 「主観で」とはいえど、なるべく有名どころのライブラリなどを参考に持ち出して執筆します
- Golangの基本文法が分かる人向け

# インデントはタブが主流
- Golangのデフォルトがタブ
    - 標準ライブラリやPlayground、有名なライブラリなどを見るとタブ採用が多い
        - 参考: [timeパッケージのSleepメソッド](https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/time/sleep.go)
        - 参考: [Go Playground](https://go.dev/play/)
        - 参考: [webフレームワークのecho](https://github.com/labstack/echo)
- タブだとGitHub上でコード読みにくい！な問題への対処法
    - `.editorconfig`ファイルを使うことで、見た目上のインデント幅を任意のサイズに指定できる
        - 参考: [pre { tab-size: 4 } · Issue #170 · isaacs/github](https://github.com/isaacs/github/issues/170#issuecomment-150489692)
        - 参考: [echoにも指定されている](https://github.com/labstack/echo/blob/master/.editorconfig)
    - または、Chrome拡張機能もある
        - [GitHub Custom Tab Size - Chrome ウェブストア](https://chrome.google.com/webstore/detail/github-custom-tab-size/jcjfkmdkcaopkioccnpbhiemfcmpnghe/related)

```golang
// Before
func main() {
    fmt.Println("スペース4つ")
}

// After
func main() {
	fmt.Println("タブ")
}
```

（エディタ上だとスペースとタブの違いわかるけど、Qiitaの記事表示だと違い伝わらない… :innocent: ）

# testは同階層でOK
- ここでいう「階層」とは「package」を指す
- 階層を分けてしまうとunexported（private）な構造体や変数を単体テストで使うためにはexport（public）が必要になる
    - 実処理としてはexport不要だが、テストのためだけにexportする、のような状況が生じてしまうので良くない
- これを避けるために同階層に置くべし
    - 下記参考を見るとそうなっている
        - 参考: [webフレームワークのecho](https://github.com/labstack/echo)
        - 参考: [csvライブラリのgocsv](https://github.com/gocarina/gocsv)
    - ただし、基本的には同階層に置いているが`tests`packageを切ってその中にもテストファイル置くパターンもある（理由はしっかり中身読めばわかる…はず…）
        - [ORMのgorm](https://github.com/go-gorm/gorm)

```
// Before
┣ repository
  ┣ campaign_repository.go
┣ repository_test
  ┣ campaign_repository_test.go

// After
┣ repository
  ┣ campaign_repository.go
  ┣ campaign_repository_test.go
```

# エラーメッセージは基本的に「小文字始まり」「句読点終わりは避ける」
- エラーメッセージ(標準だとfmt.Errorf, errors.New)に関しては、「小文字始まり」「句読点終わりにしない」、が公式の推奨
    - エラーメッセージは別のエラーメッセージやログメッセージなどの中で使われることもあるので、エラーメッセージを文章のようにしてしまうと読みにくくなるため
    - もちろん、固有名詞や頭字語はこの限りではない
    - 参考: [CodeReviewComments · golang/go Wiki](https://github.com/golang/go/wiki/CodeReviewComments#error-strings)
- ログメッセージに関してはこの限りではないが、揃えたほうがわかりやすいため、基本的に「小文字始まり」「句読点終わりにしない」で良さそう
    - こちらは諸説ありそう

```golang
// Before
err := errors.New("Required field cannot be null.")
log.Errorf("Create test file failed. (err=%+v)", err)

// After
err := errors.New("required field cannot be null")
log.Errorf("create test file failed(err=%+v)", err)
```

# エラーまわりの書き方
- こちらもCodeReviewCommentsを参考にした話
    - 参考: [CodeReviewComments · golang/go Wiki](https://github.com/golang/go/wiki/CodeReviewComments#indent-error-flow)
- エラーは即時リターン

```golang
// Before
err := db.Err
if err != nil {
	// エラーハンドリング
} else {
	// 通常処理
}

// After
err := db.Err
if err != nil {
	// エラーハンドリング
}
// 通常処理
```

- `err`変数のスコープは狭くできる
    - スコープ狭くなってヨシ :point_right:

```golang
// Before
err := db.Err
if err != nil {
	return err
}

// After
if err := db.Err; err != nil {
	return err
}
```

- ただ、スコープを無理に狭くしすぎると逆に読みにくくなるので注意

```golang
// Before
if x, err := f(); err != nil {
	return err
} else {
	// use x
}

// After
x, err := f()
if err != nil {
	return err
}
// use x
```

# 変数名
- Golangでは略語は全部小文字 or 大文字に
    - `ID, id`→`Identifier`の略語
    - `CV, cv`→`Conversion`の略語
    - 参考: [CodeReviewComments · golang/go Wiki](https://github.com/golang/go/wiki/CodeReviewComments#initialisms)

```golang
// Before
var campaignId int64
var CvData struct{}

// After
var campaignID int64
var CVData struct{}
```

# import, type, const, var句あたりの順番
- 標準パッケージ参考にすると`import`→`type`→`const`→`var`→`func`の順になっている
    - 参考: [zoneinfo.go - Go](https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/time/zoneinfo.go)

```golang
package time

import (
	"toto"
	...
)

type Titi struct {
	name string
	...
}

const (
	tata = ""
	...
)

var (
	tutu = ""
	...
)

```

# import句内部の記述順
- 標準パッケージの塊を先頭に、あとは他の塊ごとに空行を挟んで記述し、それぞれの塊の中ではアルファベット順で記述
    - 参考: [CodeReviewComments · golang/go Wiki](https://github.com/golang/go/wiki/CodeReviewComments#imports)

```golang
// Before
import (
	"os"
	"fmt"
	"github.com/toto/titi"
	"github.com/tata/tutu"
	"log"
	"myapp/foo"
	"myapp/user"
	"net/http"
)

// After
import (
	"fmt"
	"log"
	"net/http"
	"os"

	"myapp/foo"
	"myapp/user"

	"github.com/tata/tutu"
	"github.com/toto/titi"
)
```

# 定数はまとめる
- constは複数ある場合はまとめる
    - 同カテゴリである場合はまとめて、同カテゴリではない場合は別でまとめる
- 変数varについても同様

```golang
// Before
const expireDate = "2006-01-02T15:04:05"
const maxLength = 100
const MethodGet = "GET"
const MethodPost = "POST"


// After
const (
	expireDate = "2006-01-02T15:04:05"
	maxLength = 100
)

const (
	MethodGet = "GET"
	MethodPost = "POST"
)
```

# swtich-case文
- これは基本文法だけど、別の条件で同じ処理の書き方忘れがちなので載せる
    - ちなみに、Golangではcaseごとでbreakするのでfallthroughしたい場合はcase文の最後に`fallthrough`を明記する

```golang
// Before
switch type {
case enum.ONE:
	fmt.Println("hoge")
case enum.TWO:
	fmt.Println("hoge")
case enum.THREE:
	fmt.Println("fuga")
default:
	fmt.Println("piyo")
}

// After
switch type {
case enum.ONE, enum.TWO:
	fmt.Println("hoge")
case enum.THREE:
	fmt.Println("fuga")
default:
	fmt.Println("piyo")
}
```

# 合わせて読みたい
- [CodeReviewComments · golang/go Wiki](https://github.com/golang/go/wiki/CodeReviewComments) 
    - 本記事でもかなり参考にしているので原文を是非
- [Go言語の記述の迷いどころについて](https://zenn.dev/nobonobo/articles/19c84c231aff46)
    - この記事書いた後に見つけた
- [「Go言語らしさ」とは何か？　Simplicityの哲学を理解し、Go Wayに沿った開発を進めることの良さ - エンジニアHub｜Webエンジニアのキャリアを考える！](https://eh-career.com/engineerhub/entry/2018/06/19/110000)
    - Golang書くならこの辺も読みたいかも