LoginSignup
40
21

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-02

この記事は Goのカレンダー | Advent Calendar 2021 - Qiita の3つ目のカレンダーの3日目です。

これはなに?

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

インデントはタブが主流

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

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

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

testは同階層でOK

  • ここでいう「階層」とは「package」を指す
  • 階層を分けてしまうとunexported(private)な構造体や変数を単体テストで使うためにはexport(public)が必要になる
    • 実処理としてはexport不要だが、テストのためだけにexportする、のような状況が生じてしまうので良くない
  • これを避けるために同階層に置くべし
    • 下記参考を見るとそうなっている
    • ただし、基本的には同階層に置いているがtestspackageを切ってその中にもテストファイル置くパターンもある(理由はしっかり中身読めばわかる…はず…)
// 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
  • ログメッセージに関してはこの限りではないが、揃えたほうがわかりやすいため、基本的に「小文字始まり」「句読点終わりにしない」で良さそう
    • こちらは諸説ありそう
// 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)

エラーまわりの書き方

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

// After
err := db.Err
if err != nil {
    // エラーハンドリング
}
// 通常処理
  • err変数のスコープは狭くできる
    • スコープ狭くなってヨシ :point_right:
// Before
err := db.Err
if err != nil {
    return err
}

// After
if err := db.Err; err != nil {
    return err
}
  • ただ、スコープを無理に狭くしすぎると逆に読みにくくなるので注意
// Before
if x, err := f(); err != nil {
    return err
} else {
    // use x
}

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

変数名

// Before
var campaignId int64
var CvData struct{}

// After
var campaignID int64
var CVData struct{}

import, type, const, var句あたりの順番

  • 標準パッケージ参考にするとimporttypeconstvarfuncの順になっている
package time

import (
    "toto"
    ...
)

type Titi struct {
    name string
    ...
}

const (
    tata = ""
    ...
)

var (
    tutu = ""
    ...
)

import句内部の記述順

  • 標準パッケージの塊を先頭に、あとは他の塊ごとに空行を挟んで記述し、それぞれの塊の中ではアルファベット順で記述
// 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についても同様
// 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を明記する
// 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")
}

合わせて読みたい

40
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
21