他言語プログラマが最低限、気にすべきGoのネーミングルール

公開:2020/11/14
更新:2020/11/16
8 min読了の目安(約7300字TECH技術記事 2

概要

タイトルの通り、他言語から入門した人が最低限気にするべき、ネーミングルールをまとめました。

対象読者

Go の基本構文を理解している方を対象読者としています。

この記事で説明する事、説明しない事

説明する事

Go のファイル名、変数名等の名前付けに関するルールや慣例等を説明します。

説明しない事

名前付け以外で気をつけるべき Go の書き方[1] がいくつかあります。

しかし、それらに関してはこの記事では説明しません。

筆者のバックグラウンド

プログラマ歴はもうすぐ 8 年程で、Go の他には以下のような言語の経験があります。

  • JavaScript
  • TypeScript
  • PHP
  • Ruby
  • Java
  • Scala

Go は少し前に書いて、一時期書かない時期が続いていましたが、最近また書いています。

トータルすると Go の経験は 1 年半程度です。

意識すべき名前付けルール

package名

利用して良い文字列

Effective Go では簡潔で明確な名前が望ましいとされています。

小文字のみを利用し、1 単語(単数形)で構成されるのが望ましいです。

  • 良い例
    • time
    • http
  • 悪い例
    • computeServiceClient(キャメルケースは使わない)
    • priority_queue(スネークケースは使わない)

ただし 1 つ例外があります。

それは _test サフィックスを付けるパターンです。

Go は同じディレクトリ以下に複数の package 名を許可しませんが、 _test を付けるとそれが可能になります。

異なるパッケージになるので、非公開メソッドなどは参照できなくなります。

しかし、これを利用する事で循環参照の問題を回避出来る等のメリット[2]があります。

これは標準 package でも利用されているテクニックです。

// (例) heap packageのテストなので、heap_test になる
package heap_test

避けるべき単語

また以下のような名前は使わないようにするのが無難です。

  • base
  • util
  • common
  • lib
  • misc

これらの名前は「簡潔な名前」ですが、「明確な名前」ではないからです。

命名に迷ったら、Go の標準 package 名を参考にすると良いでしょう。

ファイル名

Go の公式情報で「ファイル名をどうすべきか」という記載は見つける事が出来ませんでした(もしあったらコメントで教えて下さい)

kubernetesterraform 等の有名 OSS や標準 package でスネークケースの命名が使われているので、それに従うのが無難そうです。

(例)

  • addressed_types_test.go
  • addressed_types.go

ディレクトリ名

ファイル名と同じく、「Go の公式情報でどうすべきか」という記載は見つける事が出来ませんでした(もしあったらコメントで教えて下さい)

こちらに関しては、package 名と同じく簡潔で明確な 1 単語での名前を付けるのがベストです。

なので小文字の英字のみを利用、というのが正しそうです。

kubernetesterraform 等の有名 OSS でケバブケース(単語の区切りに - を利用)を使っているパターンを見つけたました。

どうしても単語の区切りが欲しい場合はケバブケースを使うのもアリかもです。

(例)

自分はケバブケースを採用せずに、全て小文字で統一するようにしています。

ちなみに package 名とディレクトリ名が違っていても問題はないですが、合わせておいたほうが無難です。

関数、type、構造体

キャメルケースで命名します。

外部に公開する関数や構造体の場合、先頭を大文字にするという言語仕様があるので、それに合わせてアッパーキャメルケース(先頭大文字から始まる)かローワーキャメルケース(先頭小文字から始まる)が決まります。

// packageの外に公開する場合
func Contents(filename string) (string, error) {}

// package内でのみ利用する場合
func contents(filename string) (string, error) {}

レシーバ名

英語 1 文字か 2 文字でなるべく短く命名します。

型が Client であれば c, cl 等です。

レシーバ名は必ず統一する必要があります(場所によって c が使われていたり cl が使われていたりは NG)

また、修飾語を利用しない点が重要です。

例えば httpClient ならレシーバ名は c になりますし、 DBCreator なら c になります。

変数・引数名

こちらも短い変数名を推奨します。

引数に関しては、レシーバ名と同じく 1 文字を使って良いでしょう。

変数名を出来る限り短い名前が理想とはされていますが、スコープには注意しましょう。

スコープが大きな関数で短い変数を使うと可読性が大きく低下してしまいます。

また略語についても注意が必要です。

下記のようにプログラマの間で一般的になっている略称を使う分には問題ないですが、無理やり略してしまい、意味が分からない変数名になると可読性が低下します。

  • Configconf
  • Stringstr

個人的な意見ですが、もしも良い略語が思いつかないなら、普通に英単語を使った変数名が無難でしょう。

短い変数名が推奨されているからと言って、命名規則を軽視して良いという話ではないので、このあたりは注意が必要です。

error変数名

error を変数として宣言する場合は、Err prefix を付けて宣言します。

明確にこうしましょうと、記載されている訳ではないですが、Go の公式ブログ Working with Errors in Go 1.13 でこのような変数名を使っている例が確認出来ます。

また、Effective Go にも以下のような記述があります。

// Error codes returned by failures to parse an expression.
var (
    ErrInternal      = errors.New("regexp: internal error")
    ErrUnmatchedLpar = errors.New("regexp: unmatched '('")
    ErrUnmatchedRpar = errors.New("regexp: unmatched ')'")
    ...
)

ちなみに Go のエラーハンドリングは err をチェックしてエラーが nil かどうかを確認してエラーが起きていないかどうかを確認します。

その為、以下のようなコードがたくさん出てきます。

    data, err := ioutil.ReadFile(src)
    if err != nil {
        return nil, err
    }

この時に err という変数名で受け取るのが慣例です。

Err prefix はあくまでも、error を変数として宣言する場合のルールなので上記のようなエラーハンドリングには利用しません。

以下の記事にも書かれていますが、err のスコープを if 構文の中に閉じ込めたり、 := を使った書き方を利用すれば大体のケースは err だけで済ませる事が出来ます。

(参考)「Go初心者が気を付けること」の解説 err変数名のバリエーションを増やしすぎる(ほとんどの場合「err」だけで済む)

どうしても err だけで済まないようなケースが出た場合は Err prefix を利用しても良いと思われます。

ちなみに kubernetesterraform 等の有名 OSS では Err prefix を使った方法は採用されていませんでした。

このあたりをどうすべきかは明確な決まりはなさそうなので、プロジェクト毎に合わせるで良いと思われます。

map 等の存在チェック

以下のように特定のキーが存在するかどうかをチェックする際には ok という変数名を使うのが慣例のようです。

id, ok := users[userID]

これはどこかに明記されている訳ではないですが、標準 package の中で、このパターンの書き方が多く使われています。

頭文字や頭文字をとった名前の単語の命名について

下記に記載されている内容です。

https://github.com/golang/go/wiki/CodeReviewComments#initialisms

Go の命名は基本的にキャメルケースですが、元々略語として浸透している単語は一貫した大文字と小文字を使います。

url ではなく URL を使うとか、http ではなく HTTP を使うといった内容です。

個人的意見ですが、このルールを理解するのが最初難しかったです。

例えば、GitHub とか Twitter 等の単語も github とか twitter を使ったらダメなのか、等色々と混乱しました。

golangci-lint というツールでこの表記をチェックしている箇所があるのですがカバーされている単語は下記の通りでした。

(該当箇所、以下抜粋)。
https://github.com/morix1500/lint/blob/master/lint.go#L743

var commonInitialisms = map[string]bool{
	"ACL":   true,
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SQL":   true,
	"SSH":   true,
	"TCP":   true,
	"TLS":   true,
	"TTL":   true,
	"UDP":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
	"XMPP":  true,
	"XSRF":  true,
	"XSS":   true,
}

このようなルールは golangci-lint で機械的にチェックしたいですが最近よく出てくる gRPCGraphQL 等の単語はチェックされていないようです。

また自動で Go のコードを生成するツールの一部もこのルールを無視したコードが生成されたりする場合もあります。

自分は golangci-lint のこのルールを OFF にして普通にキャメルケースで命名するように統一しています。

参考にした情報

この記事を書く際に以下の情報を参考にさせて頂きました。

公式情報

個人ブログ等

あとがき

Go の命名規則についてまとめました。

ここに上げたルールの一部は golangci-lint でチェック出来ます。

まだ作成中ですが、今後は golangci-lint の設定等も記事にしようと思っています。

最後まで読んで頂きありがとうございます。

脚注
  1. panicを使わないとか、正規表現は遅いので出来るだけ避ける等色々ある ↩︎

  2. こちら のように export_test.go を利用して非公開機能をテストしたりも出来る ↩︎