脱Go言語初心者への道のり #1 〜オフィシャルガイドラインの作法【前編】〜

eureka, Inc.
Eureka Engineering
Published in
8 min readJan 14, 2016

こんにちは!エウレカの田野です。

エウレカではPairsの開発に2014年からGo言語を採用してきました。現在はPairs台湾のプロダクション環境で運用しています。その過程でプロダクションコードを書きかたのノウハウを蓄積してきました。

そのノウハウを「脱Go言語初心者への道のり」という連載でご紹介してまいります。
第1回目ですが、オフィシャルガイドラインに従ったGoコーディングの作法 について紹介いたします。

Go言語の公式リポジトリでは、コードレビューにおいてよく指摘されることについてまとめた「Go Code Review Comments」というガイドラインを用意しています。

このガイドラインに従ってGo言語のコーディングを行うことで、誰でも「良いコード」を書くことができます!

Go言語における良いコードとは?

私見ですが良いコードというのは、以下のようなコードと考えています。

  • 標準的なコード規約に準拠している。
  • 誰が書いても似たようなコードになり、他の人も読みやすい。
  • マシンリソースに配慮している。
  • 適切なエラーハンドリングできており、保守しやすい。

Go言語ではgofmt, golintなど、Code formatterやLinterが標準で用意されており、これらのツールを使えば、初めてGo言語を書く人でも簡単に良いコードを書くことができます。

※「良いコード」については、このブログの内容が個人的によくまとまっていると思います。プログラミング中級者に読んでほしい 良いコードを書くための20箇条

紹介したい項目が多いので、前後編でお送りします!

Code formatterにgoimportsを使う

Go言語の標準Code Formatterはgofmtですが、「Go Code Review Comments」ではgofmt互換ツールであるgoimportsを推奨しています。
理由はgofmt相当のコードフォーマットに加えて、以下の機能を持っているためです。

  • 使っていないimportを削除する。
  • importを標準パッケージと外部パッケージで分ける。
  • importをパッケージ名のアルファベット順にソートする。

各種エディタの設定で、保存時にgoimportsを自動的に実行するのが好ましいとされています。

参考:いくつかのエディタでgoimportsを自動実行するための設定方法を紹介します。

vim

vim-goを導入し、以下の設定をします。

let g:go_fmt_command = “goimports”
let g:go_fmt_autosave = 1

SublimeText

GoSublimeを導入し、以下の設定をします。

“fmt_cmd”: [“goimports”],

参考: SublimeText3でのGo言語開発環境セットアップ

Atom

go-plusを導入し、以下の設定をします。

  • Run Format Tool on Save にチェックを入れる。
  • Format Toolgoimportsと設定する。

参考: AtomでのGo言語開発環境セットアップ

IntelliJ

External Toolを導入し、Save動作をMacro登録したものに置き換える必要があります。

参考: Intellij IDEA でのGo言語開発環境セットアップ

中身のないスライスの宣言は、メモリを確保しないように書く

中身のないスライスの宣言は、以下のような書き方が好ましいとされています。

var t []string // 好ましい
t := []string{} // 好ましくない

var t []stringの書きかたは、スライスに値を追加するときに初めてメモリを確保します。

安易にPanicを使わない

Go言語の思想として、Fatalでないエラーをerror型の変数でreturnすることをエラーハンドリングの原則としています。Panicを利用すると、Recoverしないかぎりはそれ以上のエラーハンドリングができず、プロセスが停止します。

例えばWebアプリケーションであれば、controller層までerrorを返してinternal server errorで返すなどの処理をするのが好ましいです。
Go言語は関数の戻り値を複数個返すことができますので、返り値にerrorも返すようにしましょう。

errorPanic、どちらを使うべきなの? という疑問の回答は、このブログが参考になりました。 => Golang の defer 文と panic/recover 機構について

エラーを握りつぶさない

Effective Goのエラーハンドリング にもありますが、Go言語に慣れると関数の返り値であるエラーを_で握りつぶしがちです。

// エラーを握りつぶしている例
num, _ := strconv.Atoi("-42")

確実なエラーハンドリングをするために、エラーを握りつぶす行為はやめましょう。
標準出力エラーにすらエラーが出力されないので、問題発見の遅れにつながります。

余談ですが、カンファレンスなどでGopher同士が集うと、「エラーを握りつぶしてデバッグが困難になった!」という話題で盛りあがりがちですw

import文は空行を使ってまとめる

import文は空行でグルーピングしましょう。標準パッケージは先頭のグループに書きます。

package mainimport (
"fmt"
"hash/adler32"
"os"
"appengine/foo"
"appengine/user"
"code.google.com/p/x/y"
"github.com/foo/bar"
)
ドットインポートはプロダクションコードに書かないGo言語のimportの際にドットインポートをすると、パッケージ名を書かずに該当のパッケージの関数をコールできます。
ドットインポートを使うと、インポートの循環参照を解消できますが、「循環参照を解消しないといけない」ということは、基本的なモジュール設計が間違っている可能性が高いため、モジュール設計の再検討をまず行うべきです。
ドットインポートはテストコードで使う程度にとどめ、プロダクションコードで使うのはやめましょう。package foo_testimport (
"bar/testutil" // also imports "foo"
. "foo"
)
この例はfoofoo_testbar/testutilをインポートしていて循環参照となりますが、fooをドットインポートして循環参照を回避しています。エラーのif文でelseの利用を避けるelseの利用は避けてreturnを使いましょう。elseの中にifを書いていくと、ネストが深くなり可読性が落ちるためです。
不要なelseはgolintがチェックしてくれます。
// elseを使った書きかた
if err != nil {
// error handling
} else {
// normal code
}
// 上記はreturnを使って書き変える
if err != nil {
// error handling
return // or continue, etc.
}
// normal code
またif文の条件式内で;を使うと変数のスコープを限定できますが、そのためにelseが生じるのであれば、変数のスコープを広めることを検討するべきです。// xのスコープが限定的なので、elseを使わざるを得ない
if x, err := f(); err != nil {
// error handling
return
} else {
// use x
}
// 上記のコードは `x, err := f()` を外に出して使う
x, err := f()
if err != nil {
// error handling
return
}
// use x
まとめいかがでしたでしょうか。
ここで紹介したいガイドラインの項目は全部で15個あり、残り7項目の紹介も後編コンテンツで近日公開予定です。
内容は以下です、お楽しみに!
  • 省略語は大文字・小文字を統一する
  • 変数の命名はパスカルケースもしくはキャメルケース
  • パッケージネームと変数名(関数名)の命名は被らないようにする
  • メモリサイズの小さいオブジェクトはポインタで渡さない
  • レシーバの名前はアルファベット1–2文字で統一する
  • レシーバーのタイプに値もしくはポインタを使うケースはルールがあります
  • 限定的で狭いスコープの変数名は短くする
宣伝:もくもく会のお知らせエウレカではGoもくもく会を定期的に開催していますので、ぜひお越しください!
http://eure.connpass.com/event/24488/
image_gomoku

--

--

eureka, Inc.
Eureka Engineering

Learn more about how Eureka technology and engineering