読者です 読者をやめる 読者になる 読者になる

effective-goではない何か

去年からGolangを書き散らかしてみて、だいぶセンス感じとれるようになってきたので、Golangを書くときのイディオムのようなものをまとめてみる。

Golangをコーディングするディレクトリ

簡単な、10行20行くらいで済むコードであればどこで書いてもいいのだけれども、 ライブラリだったりモジュールに分けるレベルの規模のコードだと、コーディングするディレクトリは、$GOPATH直下、または$GOPATHへのsymlinkにしたほうが良い。

例えば、ライブラリを作成しようとしてホスティング場所はGitHubだとする。 その作成しているライブラリを使ってサンプルコードを書こうとすると、 import宣言は、

import (
  "github.com/yoppi/a-library"
)

となる。 ただ、$GOPATHにないとビルドできないので、まだGitHubにpushしていない場合はもちろん、「少し修正して書き直す」という繰り返しの中でGitHubにpushしてgo getしないと手元でコード書けない、という状況は辛い。 そこで、書いているライブラリは、$GOPATH直下 -- たとえば$GOPATHが $HOME/.go/ だとすれば、$HOME/.go/src/github.com/yoppi/a-library -- に作成すると、問題なくビルドできる。 ちなみに、$GOPATHに直接作成するか、$GOPATHへsymlink張るかの流派で分かれている、模様。 僕は、symlink派。

ライブラリ兼実行コマンドありのディレクトリ構成

ライブラリとしても使うのだけど、実行コマンド(たとえばa-execという名前だとする)もあるライブラリを作るときは、

a-library
  `
  + a-exec
  |   `
  |   + main.go
  + a-library.go
  ...

と、実行コマンド名をディレクトリ名にして、直下にmain.goとするディレクトリ構成だとわかりやすいと思っている。 こうしておくことで、go get github.com/yoppi/a-library/a-exec で追加でインストールも可能になる。

ちなみに、実行コマンドだけのものを作るときでそこそこ規模が大きくなる場合、

a-exec
  `
  + bin
  |   `
  |   + a-exec.go
  + module.go
  ...

みたいな構成にしている人が多そう。

構造体を生成するコンストラクタ

構造体を生成するコンストラクタ関数名には、型名に接頭語Newを付けることにしている。 むしろ、それ以外の接頭語が付いている名前だと、他の人が書いたものを読んだ時に戸惑ってしまう。 Golangで組み込み関数であるnew()もあることですし。 そして、この関数は型宣言した、直下に宣言しておくとさらに読む人が把握しやすいので便利。

type A struct {
  // ... member
}

func NewA() *A {
  a := &A{}
  // ...
  return a
}

型T

Golangで、任意の型を表現するのはinterface{}。reflectを頻繁に使って問題を解決しようとするプログラミングだと、intarface{}型を駆使することになる。 これを毎回毎回タイプしてたらFPの消費量激しくなって、辛くなってくるので、aliasとして型Tを宣言しておくと便利だと思う。

type T interface {}

init関数でコマンドライン引数をパースする

Golangでは、init()関数は特別扱いされる。 使い道としては、Javaでいうところのstatic initializerに相当するもの。 たとえば、ライブラリ内コレクションを貯める、みたいなインタフェースがあったとして

package some-library

var hogeCollection map[string]Hoge

func init() {
  hogeCollection = map[string]Hoge{}
}

のように書いておくと安全に初期化できる。 これを利用してコマンドライン引数の初期化などを書いておくとコードがすっきりしそう。 コマンドライン引数の扱いは、flagパッケージを駆使すると便利にパースできる。

import (
  "flag"
)

var (
  a int
  b string
)

func init() {
  flag.IntVar(&a, "a", 0, "args a")
  flag.StringVar(&b, "b", "", "args b")
  flag.Parse()
}

func main() {
  println(a)
  println(b)
}

グローバルに変数を宣言することになるけど、mainに書くよりかは読みやすいかなぁと。