Go初心者が気を付けること


Go初心者がやってしまいがちなやらない方がいいことを書き出してみました。

情報検索や環境構築

  • golang.jpを見に行ってしまう
  • depが最新推奨のパッケージマネージャだと勘違いする(Go標準の「go mod」を使おう)
  • 「GO???」環境変数を理解せずに設定しまくる(わからない場合は一切設定しないのが正しい)
  • しょっぱなからgvm,gobrew,goenvなどのマルチバージョンのマネージャを入れようとしてエディタ連携環境構築に失敗する (複数バージョンのGoの運用は既に標準のGoだけでできるようになっている)
  • エディタにgoimportsやgolintを設定し忘れる
  • OSのパッケージマネージャまかせで古いGoやgccgoをインストールしてしまう

エラーハンドリング周り

  • err変数名のバリエーションを増やしすぎる(ほとんどの場合「err」だけで済む)
  • サンプルのコピペで「エラーの握り潰し」ごとコピってしまう
  • エラーの種別判定にメッセージの文字列を使う
  • nilなエラーをラップしてしまう
  • panic/recoverを多用しちゃう

Goの仕様への理解不足

  • deferの呼ばれるタイミングを関数スコープより狭いスコープと勘違いする
  • 型付nilをinterface型に変換してしまいnil比較を困難にしてしまう
  • インターフェース型にまでポインタ宣言を持ち込もうとする (正しくは「インターフェース型はポインタ値も非ポインタ値も内包する概念」)
  • 多値返しをタプルオブジェクトのように扱おうとしてしまう
  • mapの要素が非ポインタ構造体値のフィールド(配列値の要素も同様)にアクセスしようとしてエラーに遭遇 (mapに複合の値を入れたり出したりするときは値丸ごとでしか行えない)
  • 同名変数のシャドウイングを理解せずに使ってしまう
  • jsonのオプジェクトを構造体で定義するとき、小文字のフィールドを書いて、読めない書けないで困る
  • goroutineの起動時間が少しかかるのを忘れてコード書いちゃう(by shibukawa)
var wg sync.WaitGroup
go func() {
    wg.Add(1)
    :
}()
wg.Wait()

だめではないけどいいことない

  • mapを初期化し忘れる(参照操作ではpanicしないので気づくのが遅れる)
  • 最初っから細かいパッケージツリー構成を作ろうとして行き詰まる
  • インポートパスに相対パスを書いちゃう
  • chanとmutex併用を多用する
  • goroutineが止まらないライブラリを書く

パフォーマンスチューニング周り

  • 正規表現を関数内で初期化して使っちゃう(by shibukawa)
  • データベースのPrepareを使うタイミングでないところに書いちゃう
  • 測定もせずに固定値でもないスライスのキャパシティサイズを指定するチューニングを行う
  • reflectを速度の必要な場面で使っちゃう
  • 速度の必要なところでスライスの型変換が必要なデータ構造を作っちゃう
  • goroutineをやたら頻繁にスケジューラスイッチさせるような実装を書いてしまう

他の処理系の風習を持ち込む

  • 再帰呼び出しを多用する
  • 高階関数を作ろうとする
  • 自作ライブラリの利用方法をメソッドチェインで作ろうとする
  • 継承の仕組みを構造体のembedで実現しようとしてしまう(by hnakamur2)

httpクライアント処理

  • resp.Statusを確認しない
  • 適切なタイミングで「defer resp.BodyClose()」を呼び忘れる

まとめ

いろんな観測されたつまづいているポイントを収集していたものをまとめました。 他にあればまた教えてください。