Effective Go とは
明快で慣用的なGoコードを書くためのTips
*日本語訳はこちら
事前に見ておくと良いもの
A Tour of Go:Goチュートリアル
How to Write Go Code: 基礎
The Go Programming Language:言語仕様
チートシート
注: 筆者の主観により、項目・内容を取捨選択しました
整形
コメント
コメントの頭に、記述する「項目名」を書くようにする
↓
$ godoc 項目名 | grep -i parse
が便利になる (ドキュメント)
宣言のグループ化
項目間の関係を示せる
var(
countLock sync.Mutex
inputCount uint32
outputCount uint32
errorCount uint32
)
ゲッターとセッター
例えばフィールド名が"owner"の場合、以下のように命名する
- ゲッター: Owner()
- セッター: SetOwner()
命名
○ MixedCaps
○ mixedCaps
△ mixed_caps
再宣言と再割り当て
このようなコードは合法であり、よく用いられる
f, err := os.Open(name)
if err != nil {
// hogehoge
}
d, err := f.Stat()
Type switch
switchは、インタフェース変数の動的な型を見つけるために利用することもある
switch t := interfaceValue.(type) {
default:
fmt.Printf("unexpected type %T", t) // %T は型を出力する
case bool:
fmt.Printf("boolean %t\n", t)
case int:
fmt.Printf("integer %d\n", t)
case *bool:
fmt.Printf("pointer to boolean %t\n", *t)
case *int:
fmt.Printf("pointer to integer %d\n", *t)
}
Defer
deferを実行した関数がリターンする直前に、指定した関数を呼び出してくれる
ファイルのClose、ミューテックスのアンロックなどで用いられる
func Contents(filename string) (string, os.Error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // f.Closeは、完了時に実行される
// 何らかの処理
return "hoge", nil // ここで、fはクローズされる
}
遅延指定された関数の引数は、関数の実行時ではなく、deferを実行した時に評価される
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
// 出力結果
entering: b
in b
entering: a
in a
leaving: a
leaving: b
データ
newによる割り当て
new(T): 新しく割り当てられたT型のゼロ値のポインタを返す
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
p := new(SyncedBuffer) // *SyncedBuffer型
var v SyncedBuffer // SyncedBuffer型
makeによる割り当て
make(T): 初期化された(=ゼロ値でない)T型の値を返す
makeで割り当てできるのは、スライス・マップ・チャネルだけ
この3つの型は使用前に初期化されている必要があるため、"make"が存在している
newとmakeの違い
// 必要以上に複雑な書き方
var p *[]int = new([]int) // スライス構造の割り当て(*p == nil)
*p = make([]int, 100, 100)
// 一般的な書き方
v := make([]int, 100) // スライスvは、100個のintを持つ配列への参照
コンストラクタと複合リテラル
複合リテラル: 実行する度に新しいインスタンスを生成する式
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0}
return &f
}
C言語とは異なり、関数から戻った後も、変数に割り当てたメモリは生き残る
配列とスライス
配列 | スライス |
---|---|
値 | 参照型 |
メモリの配置指定や割り当て回避ができる | 配列をラップしたものであり、Goで多用される |
初期化
定数の列挙「iota」
type ByteSize float64
const (
_ = iota // 1番目の値(0)を無視
KB ByteSize = 1<<(10*iota)
MB
GB
TB
)
init関数
用途は主に2つ
- 宣言として記述できない初期化処理を行う
- 処理を開始する前に、状態のチェック/修正を行う
func init() {
if USER == "" {
log.Fatal("$USER not set")
}
if HOME == "" {
HOME = "/usr/" + USER
}
if GOROOT == "" {
GOROOT = HOME + "/go"
}
// GOROOTはコマンドラインから--gorrotフラグを指定することで上書き可能
flag.StringVar(&GOROOT, "goroot", GOROOT, "Go root directory")
}
その他
以下は後日追記するかも
- インターフェース
- 埋め込み
- 並列性
- エラー処理