【読書感想ブログ】Go言語プログラミングエッセンス - mattn / 技術評論社

Go言語といえば mattnさん、mattnさんといえばGo言語。それくらいには mattnさんが書かれるGo言語の書籍には信頼がありますが、そんな mattnさんが新たに出版された書籍が「Go言語プログラミングエッセンス」です。しかも単著。これは買うしかないですよね。そして読むしかないですよね。なので読んでみた感想です。

はじめのほうには Go言語がなぜ作られたのか、どういう言語なのかというところ、または文法的なところが書かれていました。mattnさんはブログもされていて、たまにポストされるんですが、私でもわかるような書き口で書いてくださっていて助かるな~といつも思っているんですが、その書き口がこの書籍にも反映されていて、内容がスラスラ入ってくる感じになってます。

この書籍のいいところは、具体的なコードが多く書かれていて手を動かしながら理解できる点が良いと思いました。また、簡単なWebアプリケーションやCLI題材によく使われるパッケージの紹介等もされていてよかったです。

あとは、Go言語に限らないテクニックなんじゃないかな?と思う部分もあり、たとえば「推測するな計測せよ」なんていう思想はほかの言語でも通用しそうですよね。そういったプログラミングをする上での大事な考え方も学べて良い本だなぁ~と思いました。

個人的に今後に役立ちそうだなぁと思った部分の抜き書き

go get でバージョン指定

go get github.com/mattn/go-runewidth@v0.0.12 のようにするとバージョンを指定して go get できる

最新バージョンにバグがあったり、破壊的な変更がされていたりする時にこういう対応をするとよい

vendor ディレクト

go.mod と同じディレクトリにあるvendorというディレクトリに置かれたモジュールのソースコードは優先して参照されます

以下のコマンドを実行すると、go.mod と go.sum に書かれたモジュールが一括で vendor ディレクトリにダウンロードできます

$ go mod vendor

おまじない

おまじないとして次のようにインターフェースIの型を持つ変数_に型fooのポインタ型nilを代入しておくことで、わざわざコンパイルをしなくても、IDEがエラーを検知して間違いに気づくことができます

recoverの使いどころ

通常、意図しないpanicが起きうるということは、そのプログラムにはバグがあることを意味するため、不用意にrecoverを呼び出すべきではありません。プログラムは落ちるべくして落ちたほうがよく、それを止めるべきではありません。

Explicit is better than implicit, Simple is betetr than complex

明示的であることは暗黙的であることよりも優れている。単純であることは複雑であることよりも優れている

Goにおける日付時刻の書式

「1月2日3時4分5秒2006年」と連番になっており、おおよそ表4.1のように理解すると覚えやすくなっています。

書式 連番 意味
1 1
01 1 月のゼロ埋め
2 2
02 2 日のゼロ埋め
3 3
03 3 時のゼロ埋め
15 3 時のゼロ埋め(24時間表記)
4 4
04 4 分のゼロ埋め
5 5
05 5 秒のゼロ埋め
2006 6
+007 7 タイムゾーン
JST - タイムゾーン

timeパッケージに標準で用意されている書式

取り扱いづらそうに見えますが、多くの場合はtimeパッケージに標準で用意されている固定の書式名を使うことができます

書式名 フォーマット
Layout 01/02 03:04:05PM '06 -0700
ANSIC Mon Jan \_2 15:04:05 2006
UnixDate Mon Jan \_2 15:04:05 MST 2006
RubyDate Mon Jan 02 15:04:05 -0700 2006
RFC822 02 Jan 06 15:04 MST
RFC822Z 02 Jan 06 15:04 -0700
RFC850 Monday, 02-Jan-06 15:04:05 MST
RFC1123 Mon, 02 Jan 2006 15:04:05 MST
RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700
RFC3339 2006-01-02T15:04:05Z07:00
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
Kitchen 3:04PM
Stamp Jan \_2 15:04:05
StampMilli Jan \_2 15:04:05.000
StampMicro Jan \_2 15:04:05.000000
StampNano Jan \_2 15:04:05.000000000
DateTime 2006-01-02 15:04:05
DateOnly 2006-01-02
TimeOnly 15:04:05

time.ParseDuration

d, err := time.ParseDuration("3s")

3秒であれば time.Second * 3 と書くことができます。この 3s や 4m といった記法は、ユーザーから経過時間を指定してもらう際にとても便利で、例えばUNIXの sleep コマンドにも採用されています

context.Context

context.Context は習慣的に関数の第一引数として渡され、仮想の関数に引き渡されます。

どこに書くかいつも悩んでいたのでこういう慣習を知れたのはよかった

tag付ビルド

これらのソースコードは -tags cat フラグを付けてビルドすると cat.go が有効になり、dog.go は無効になります。

$ go build -tags cat

Functional Options Pattern

Server struct に公開フィールドとして Timeout や Logger を足したり、SetTimeout や SetLogger メソッドを追加したりすることはできます。しかし、利用者にタイムアウト値やロガーを任意のタイミングで変更させたくはありません。できれば New で指定した初期値から変更させないように制限したいですね。

Golang Functional Options Pattern | Golang Cafe

DDDとの親和性高そう

Method Value

少し混乱するかもしれませんが、Goのメソッド呼び出しは、第一引数にレシーバに持った関数の呼び出しと同義であるといえます。

type I int

func (i I) Add(n int) I {
  return i + I(n)
}

func main() {
  n = 1
  fmt.Println(n.Add(2))
  fmt.Println(I.Add(n, 2))
}

I.Add(n, 2) なんて呼び出しができるということを初めて知った

goroutine

goroutine は「ファイル I/O やネットワーク通信でブロックしている間にもランタイムが選んだ CPUコアで別の処理を平行して行うことで単純な並列処理よりも効率的に処理を行う」を主題にしているランタイムの機能です

これを知っているとゴルーチンをむやみやたらに使うことも減りそう

channel by range

ch := make(chan []byte)

for _, b := range ch {
}

channel を for で使えるの初めて知った

テストのパッケージ

テストをする際には、パッケージ名の指定に2つの方法があります。

  • テスト対象と同じパッケージ名称でテストコードを書く
  • テスト対象とは別のパッケージ名称でテストコードを書く

前者はパッケージ名称が同じですので、private関数もテストすることができます。一方、後者はあえて別名を付けることにより、privateな変数や関数にアクセスできないテストを書くことがるため、このパッケージの使用者と同じ実装をサンプルコードとして明示することができます

t.Skip / t.Skipf

テストによっては特定のOSでは実施できないものもあります。そういった場合には t.Skip または t.Skipf を使うことができます。

t.Short

-short を付けて実行した場合には testing.Short() が true を返します。各テストで -short のときにはスキップしたい場合には、t.SkipNow() を呼び出します。

t.Parallel

テストを並列実行したい場合に使うらしい

もう一つの注意点は、Table Driven Tests を平行で実行する際にはループの中で test変数を束縛することです。

for _, test := range tests {
  test := test // ← ★
  t.Run(test.name, func(t *testing.T) {
    t.Parallel()
    got := Add(test.lhs, test.rhs)
    if got != test.want {
      t.Errorf("%v: want %v, but %v:", test.name, test.want, got)
    }
  }
}

★部分の処理を入れないと、タイミングによってはすべてのテストが tests に含まれる最後の項目を参照してしまいます。

これは、今後注意しなくてもよくなるかも?なぜなら、loopvar という機能が今後入る可能性があるから。そのあたりも、mattnさんが Forkwell の Youtubeで話されていた。

Go言語プログラミングエッセンス - Forkwell Library#27 - YouTube

Goの標準外のパッケージ

パッケージ 説明
golang.org/x/arch マシンアーキテクチャ
golang.org/x/crypto 暗号化
golang.org/x/exp 実験的
golang.org/x/image 画像
golang.org/x/mod Go モジュール
golang.org/x/net ネットワーク
golang.org/x/oauth2 OAuth2
golang.org/x/sync 非同期
golang.org/x/sys システムコール
golang.org/x/term 端末
golang.org/x/text テキスト
golang.org/x/time 時間
golang.org/x/tools ツール
golang.org/x/xerrors エラー

flag.Duration / flag.DurationVar

第四章で time.Duration を紹介しましたが、これを flag として受け取ることができます

flag.Text / flag.TextVar

encoding.TextMarshaler は MarshalTextを、encoding.TextUnmarshaler は UnmarshalText メソッドを持ったインターフェースです。

type TestMarshaler interface {
  MarshalText() (text []byte, err error)
}

type TextUnmarshaler interface {
  UnmarshalText(text []byte) error
}

これらインターフェースを実装した型を flag パッケージで扱うことができます。

独自の型をテキストでパースできるのはよさそう

echo.FormFieldBinder / echo.CustomFunc()

echo でフォームデータを型にバインドするときに便利に使えるメソッドがあるみたい。

Binding | Echo

フォーム以外にも、クエリ文字列なんかもあるみたいで便利そう。

また、プリミティブな型以外にもバインドできる echo.CustomFunc() っていうのがあってこれも便利そうだった。

この書籍では inputタグでPOSTされる時刻フォーマットをパースする目的で使われていた。

私もこの問題に直面したことがあって、その時は別の方法で対処したんだけど、今後はこちらを使ったほうが見やすいコードになりそうだなと思った。

テンプレート処理

echo でテンプレートを扱いには Render メソッドを持った実装を e.Renderer に設定する必要があります。

e.Renderer = &Template {
  templates: template.Must(template.New("").
    Funcs(template.FuncMap{
      "FormatDateTime": formatDateTime,
    }).ParseFS(templates, "templates/*")),
}

こうすることで、echoからは以下のようにindexテンプレートにvalueを渡し、FormatDateTime関数を呼び出せるようになります。

return c.Render(http.StatusBadRequest, "index", value)
{{FormatDateTime .}}

echo にテンプレート渡せるのも知らんかったし、テンプレートにカスタム関数を追加できるのも知らんかった。

モジュールのメジャーバージョンアップ

ただし、go get -u は、モジュールのメジャーバージョンアップは行いません。メジャーバージョンアップをするには以下を実行します

$ go mod edit -replace=モジュール名@v=モジュール名@v

ent/ent

Ent (ent/ent) は Facebook 社が開発しているORMです。

今度何かDB関連の個人プロジェクトを作成するときに使ってみようかな。

まとめ

Go言語プログラミングエッセンスの読書感想ブログを書きました。

信頼の mattnさんの書籍で、予想通り良書でした。

Go言語のチュートリアルを一通りやって、次に読む書籍としてよいかと思いますので皆さんも読んでみてはいかがでしょうか?