kakakakakku blog

Weekly Tech Blog: Keep on Learning!

「みんなのGo言語」には現場で使える実践テクニックが本当に書いてあった

発売されたのは約1年前だけど,最近仕事で Golang を書いているので,実践的なテクニックを学ぶために今さらながら「みんなのGo言語」を読んだ.WEB+DB PRESS のようなカジュアルさのある本で,1日でサクッと読むことができた.読み終わった後に,学んだことを実際に試してみたりもして,とにかく即戦力のある1冊だと感じた.

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

golint -min_confidence

今までも golint は使っていたけど,各項目に Confidence というレベル設定があって -min_confidence オプションで閾値を決められるというのは知らなかった.golint の README にも書いてなかったけど,実際に golint.go を読んだら実装があり,デフォルトは 0.8 になっていた.もっと厳しくても良いのではないかなと思って,さっそく 0.6 に変更した.0.8 → 0.6 に変更したことによって追加で検知できたのは以下で,エラーメッセージの1文字目も小文字にするのがお作法なのかぁー,という気持ちだった.

error strings should not be capitalized or end with punctuation or a newline

golint -set_exit_status

デフォルトのまま golint を実行すると,コマンドの終了ステータスが 0 で返ってきてしまうため,今までは CircleCI で以下のようなワークアラウンド実装をしていた.

test:
  override:
    - test -z "$(golint $(glide novendor) | tee /dev/stderr)"

しかし -set_exit_status オプションを使うと,指摘が出た場合に 1 が返ると本書に書いてあった.よって,以下のように書き換えることができて非常に助かった.

test:
  override:
    - golint -set_exit_status $(glide novendor)

Makefile

最初は golint と go vet を circle.yml に直接書いていたけど,僕のチームに Golang のアドバイザーとして入ってもらっているメンバーに「Makefile で書きましょう」と教えてもらって,今は Makefile を使っている.まさに同じことが本書にも書かれていて,参考になった.なお make help の部分は本書で紹介されていた make2help は使ってなくて,以下の記事で紹介されていた方法を使っている.

postd.cc

ザッとこのような感じになり,CircleCI でも make lint を実行するだけで良いという状態になっている.

help:
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

setup: ## Setup some tools
    go get -u github.com/Masterminds/glide
    go get -u github.com/golang/lint/golint

lint: ## Run the Golint in all directories without /vendor
    go vet $$(glide novendor)
    golint -min_confidence 0.6 -set_exit_status $$(glide novendor)

goroutine の停止

goroutine をどのように停止するかという話があり,まさに似たような実装を書く予定があって,チャネルをクローズするイメージで考えていたので,Go 1.7 以降だと context を使うのが推奨であると知れたのは非常に良かった.

実際に本書に載っているコードを写経して動作確認をしてみて,これだけだとあまりチャネルをクローズする実装と変わらないと思ったけど,最後に context.WithTimeout を使えば,タイムアウトの考慮もできるようになるメリットがあるという紹介があった.

とは言え,本書だけだと記載が少なく概要レベルの紹介だったため,以下の記事を読んで,より理解を深められたような気がする.

ベンチマーク

テストの章では,ベンチマークの機能を知った.実際に写経をして試したりもした.

cat.go

package cat

import "bytes"

func cat(ss ...string) string {
    var r string
    for _, s := range ss {
        r += s
    }
    return r
}

func buf(ss ...string) string {
    var b bytes.Buffer
    for _, s := range ss {
        b.WriteString(s)
    }
    return b.String()
}

cat_test.go

package cat

import "testing"

func seed(n int) []string {
    s := make([]string, 0, n)
    for i := 0; i < n; i++ {
        s = append(s, "a")
    }
    return s
}

func bench(b *testing.B, n int, f func(...string) string) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        f(seed(n)...)
    }
}

func BenchmarkConcatenate(b *testing.B) {
    benchCases := []struct {
        name string
        n    int
        f    func(...string) string
    }{
        {"Cat", 3, cat},
        {"Buf", 3, buf},
        {"Cat", 100, cat},
        {"Buf", 100, buf},
        {"Cat", 10000, cat},
        {"Buf", 10000, buf},
    }
    for _, c := range benchCases {
        b.Run(fmt.Sprintf("%s%d", c.name, c.n),
            func(b *testing.B) { bench(b, c.n, c.f) })
    }
}

サブベンチマークという機能を使って書くことで,テーブルドリブンに書けるのは良かった.以下のような結果となり,文字列結合をする要素数が多くなればなるほど bytes.Buffer を使った方がパフォーマンスが良いということを確認することができた.すぐに仕事に活かせる知見だった.

$ go test -bench .
BenchmarkConcatenate/Cat3-8            10000000          164 ns/op         54 B/op         3 allocs/op
BenchmarkConcatenate/Buf3-8            10000000          181 ns/op        163 B/op         3 allocs/op
BenchmarkConcatenate/Cat100-8            300000         5794 ns/op       7520 B/op       100 allocs/op
BenchmarkConcatenate/Buf100-8           1000000         1938 ns/op       2160 B/op         4 allocs/op
BenchmarkConcatenate/Cat10000-8             200      7414202 ns/op   53327801 B/op     10000 allocs/op
BenchmarkConcatenate/Buf10000-8           10000       188879 ns/op     211424 B/op        11 allocs/op
PASS

その他

Windows の考慮,リフレクションの話などは,一通り読んだけど,実際に使う場面がありそうなときに改めて読み直そうかなと思っている.CLI の話は前に Togoo を実装したときに学んだ内容が多く含まれていて,復習の意味も兼ねて知識を整理することができた.

読んでいて少し気になったこと

初版だからある程度はしょうがないとは思うけど,少し誤植が多いなとは思った.また,誤植ではないものの,文字間隔が広くなっているのが気になる行もあった.これは組版の問題だとは思うけども.誤植一覧は以下に公開されていた.

まとめ

  • 仕事でさっそく使えるような,実践的なテクニックが学べる1冊だった
  • 実装のテクニックだけではなく,OSS として公開されているパッケージが多く紹介されているのも良かった

関連記事

紹介されていた「プログラミング言語Go」も購入したので,さっそく読むぞ!

suzuken.hatenablog.jp