「Effective Go」のチートシート 〜脱Go初心者〜

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")
}

その他

以下は後日追記するかも

  • インターフェース
  • 埋め込み
  • 並列性
  • エラー処理
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.