OGPは白川郷だ。石川県に住んでいたときよく白山白川郷ホワイトロードをドライブして遊びに行っていた。早くに閉まるから帰りは富山側に出て日本海側で美味しい魚を食べて石川に戻っていた。あのときは楽しかったなぁ。
今年、ISUCONに挑戦するつもりの夏休みの過ごし方をしているので Go の勉強を始めた。自分には既にNode.js とRustが Main/Sub weaponになっているので、別にISUCON向けに勉強しなくていいと思っていたが、どうも上位勢は軒並みGoっていうのと、彼らの解法ブログを読み解くためにはGoをやっていた方がよさそうだったのでGoの勉強を始めた。
ちなみにGoは生涯で3行しか書いたことがない(うち2行はHTMLテンプレート)生粋の素人だ。いや正確には遥か昔に勉強に挑戦したことがあって本を一冊買った覚えはあるが、A Tour of Go の冒頭で「ほぇ〜大文字でexportできるんだ〜」となったのでマジで何も覚えてないし初心者に間違いと思う。対戦ありがとうございました。
学んだこと
A Tour of Go をやってみて TIL ってなったところを挙げていこうと思う。
Exported names
Goでは、最初の文字が大文字で始まる名前は、外部のパッケージから参照できるエクスポート(公開)された名前( exported name )です。 例えば、 Pi は math パッケージでエクスポートされています。
そんなエクスポート方法あるんだという感動
Functions continued
関数の2つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できます。
驚き
Named return values
Goでの戻り値となる変数に名前をつける( named return value )ことができます。戻り値に名前をつけると、関数の最初で定義した変数名として扱われます。
驚き。同じ型のものを複数返したいときは何かバグを埋め込むきっかけになりそうで、自分は使わないかなと思った。
Zero values
変数に初期値を与えずに宣言すると、ゼロ値( zero value )が与えられます。
驚き
If with a short statement
if ステートメントは、 for のように、条件の前に、評価するための簡単なステートメントを書くことができます。
最初 if let ぽいと思ったけど違った。スコープを短くできるから積極的に使うべき機能な気がした。
Switch evaluation order
switch caseは、上から下へcaseを評価します。 caseの条件が一致すれば、そこで停止(自動的にbreak)します。
JS だと次の case に移れるからそこが異なる点として印象に残った。
Defer
defer ステートメントは、 defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させるものです。
最近 drop トレイトを使いこなせるようになってきてこの手の機能が欲しくなっていた。Go にもあって嬉しい。
Pointers to structs
フィールド X を持つstructのポインタ p がある場合、フィールド X にアクセスするには
(*p).X
のように書くことができます。 しかし、この表記法は大変面倒ですので、Goでは代わりに p.X と書くこともできます。
なるほどと思った。
Methods
Goには、クラス( class )のしくみはありませんが、型にメソッド( method )を定義できます。 メソッドは、特別なレシーバ( receiver )引数を関数に取ります。
ちょっと最初は詰まった。Rustにもstruct にメソッド生やすとき・traitを実装するときに &self 渡す・渡さないがあるよな〜、値渡しか参照渡しか〜、コピーしたくないよな〜っていう場面があるのを思い出してなんとなくで理解したつもり。
ただ構文がちょっと高階関数ぽい見た目をしていて最初はギョッとした。そういうものだと覚えることにしておく。
Interfaces
interface(インタフェース)型は、メソッドのシグニチャの集まりで定義します。
読み進めていてinterface/trait のようなものが欲しくなったので、欲しいものが手に入った感。
Interfaces are implemented implicitly
型にメソッドを実装していくことによって、インタフェースを実装(満た)します。 インタフェースを実装することを明示的に宣言する必要はありません( "implements" キーワードは必要ありません)。
驚き
The empty interface
ゼロ個のメソッドを指定されたインターフェース型は、 空のインターフェース と呼ばれます:
「Go にも any 型がある...」 という話を聞いていたので「これか〜」となった。ただどちらかというと unknown 型のような雰囲気を感じた。
Type assertions
型アサーション は、インターフェースの値の基になる具体的な値を利用する手段を提供します。
当然型を絞り込みたいので欲しい機能だった。
Type switches
型switch はいくつかの型アサーションを直列に使用できる構造です。
これも必要な機能だった。instance of とnarrowing, is T 相応のものがあるのかは気になっている。
sync.Mutex
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
Mutex<T>
になっていないから lock だけ取ってアクセスしないとか、lock 取らずにアクセスできそうとも思ったのだけどどうなのだろう。goroutine 周りはライブラリを使ってサーバー書く分ではユーザーが直接触れることはないって友達に言われているので、もしかしたら知る機会が来ないかもしれない。
これから
てな感じで一通りは文法はなぞったので明日から実アプリケーションを書いていく。なにか簡単なWebサーバーを自分が Node.js を使うときのクオリティで作りあげてみようと思う。
- HTTPサーバー
- DB Connection / Migration
- 境界値チェックや型推論
- テスト
- スキーマ駆動開発
- コンテナデプロイ
あたりを目指す。
なんとなく「まあいけるっしょ」的な見通しはあるが、DB周りはちょっと調べた限りで不安がある。拾ってきたコード眺めた限りではLayered な設計が好まれていなさそうなのと、Entity的なものがDBへのマッピングを持っていたり(DTO的なものが作られない?)、色々設計から勉強する必要がありそうとは思っている。(それはそうとEntityとDTO両方定義してEntity側に一方向の依存を持たせるような Layered Architectureに対しては「別にここまでしなくてもよくない?」って思うきっかけが色々あって最近考え直している事柄でもある)
あと型周りも"状態を列挙してUnionで繋げて分岐でnarrowing" だったり、"状態をenumで列挙してpattern match、ネストは and_then で flatten する" みたいなことができるのかというのは不安がある。(それに関する恨み節は既に聞かされている)
まあまだ何もわからん状態ってのと、自分で踏み抜いて発狂している時やトレードオフに引き裂かれている時が一番楽しいのでとりあえず手を動かしてみる。 Goは郷に倣えとも聞くので、Goの洗礼を早く受けたい。
おわりに
昔勉強した時は本当にJavaScriptしか知らない人間で「ポインタ?参照?ほにゅ?」という状態なので *
と &
に抵抗感があり、完全に目が滑っていた。それが今では経験値が貯まったお陰で理解できていて、裏側で何やっているか想像がつくようになったと思っている。今回はちゃんと入門できそうだ。