LoginSignup
37
40

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-01-12

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

その他

以下は後日追記するかも

  • インターフェース
  • 埋め込み
  • 並列性
  • エラー処理
37
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
40