🐱

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

8 min read 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)

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

例えば httpClientDBCreator は両方とも 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 を利用して非公開機能をテストしたりもできる ↩︎

Discussion

良記事ありがとうございます!
一点、

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

これは func contents(filename string) (string, error) {} ではないでしょうか

ログインするとコメントできます