この記事は、フラー株式会社 Advent Calendar 2021の6日目の記事です。
5日目の記事は @m-coder さんによる DroidKaigi2021に登壇した話 でした。
私は元々Ruby on Railsで開発をしていたのですが、最近転職しまして、Goに入門しました。
Goやり始めて「へぇ〜」と思ったことを書いてみます。
型があるとうれしい
私は静的型付けの言語をこれまであまり触ったことがなかったのですが、型がちゃんと決まってるとありがたいですね。
エディタ(VSCodeを使っています)の補完機能がバリバリ効いて、
- この変数にどんな値が入っているのか
- この変数はどんなメソッドが使えるか
- このメソッドにどんな値を入れればいいか
などを教えてもらえるので、サクサクコードを書き進められます。
Rubyだと「最近入力した単語」くらいしかエディタの候補に出してもらえないので、ファイルを行き来してメソッドの中身とかを確認する必要があったりしたので、そこが地味に手間だったな〜と思います。
クラスは無い
Goではクラスを定義する仕組みが無いんですね。
他の言語で言うクラス的なことをやるには、struct
(構造体)を使って任意の型のフィールドを持つ型を定義して、その型に対するメソッド定義を外側で(型定義とは別に)やる、みたいな流れになります。
例えばfoo
というstring
型のフィールドと、bar
というint
型のフィールドを持ったFooBar
という型を定義したいな〜というときはこういう風に書きます。
type FooBar struct {
foo string
bar int
}
で、FooBar
型のインスタンスに紐づくメソッド(Rubyで言うところのインスタンスメソッド)を定義したいな〜というときは、型定義とは別に、以下のようなメソッド定義を書きます。
func (f FooBar) SayFoo() {
fmt.Sprint(f.foo)
}
func
キーワードの後に(f FooBar)
とあるのが、「この関数はFooBar
型の変数f
がレシーバーになるよ」という意味で、これによりFooBar
型のインスタンスでSayFoo()
というメソッドを使用できるようになります。
結果として、以下のようにSayFoo()
メソッドを備えたFooBar
型が使用できるようになります。
func main() {
f := FooBar{foo: "Woooo!", bar: 1} // FooBar型のインスタンスを生成して、`f`に代入
f.SayFoo() // `f`のメソッド`SayFoo()`を実行
}
全体のコードとしてはこうなります。
package main
import "fmt"
type FooBar struct {
foo string
bar int
}
func (f FooBar) SayFoo() {
fmt.Print(f.foo)
}
func main() {
f := FooBar{foo: "Woooo!", bar: 1}
f.SayFoo()
}
ちなみに、同じようなことをRubyでやるとしたらこんな感じでしょうか。
class FooBar
def initialize(attr = {})
@foo = attr[:foo].to_s
@bar = attr[:bar].to_i
end
def say_foo()
puts @foo
end
end
foo_bar = FooBar.new(foo: "Woooo!", bar: 1)
foo_bar.say_foo
Goの方が型定義とメソッド定義が分離されてスッキリしてて良いね〜ということなんでしょうかね。
変数名は短くする
Go界隈だと、割と短い変数名をつける風潮があるように感じます。1文字だったり略語にしたり。
Rubyだと2単語をアンダーバーでつなげた変数名とかを良く書いていた気がします。
型が明確になっているし、説明的な変数名をつけないといけないような見通しの悪いメソッドは書くもんじゃねえぞ、というノリなんでしょうかね。
まあGoに入ればGoに従えということで。
略語は全部大文字にする
Goでは複数単語をつなげた名称をつけるときはキャメルケースにする風習です。
その際にID
やURL
といった、略語はすべて大文字のままにしておくのが良しとされているようです。
例えばuserID
とかbaseURL
みたいな感じです。
JavaScriptなんかだとuserId
とかbaseUrl
みたいにしちゃいそうな気がしますが、まあGoに入ればGoに従えということで。
構造体のタグというものがある
Goの構造体のフィールド定義では、型に加えて「タグ」と呼ばれる情報を付与することができます。フィールドのオプションみたいなもののようです。
例えばGoの標準パッケージであるjsonパッケージを使うときに、読み込むJSONのキーと構造体のフィールド名を関連付けるのに使われます。
こんな感じで書き、json:"id"
という部分がタグです。
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
こうすることで、「JSONの"id"というキーが、User
型のID
フィールドに対応しているよ〜」ということがjsonパッケージに伝えられて良い感じにやってくれるようになります。へぇ〜。
ディレクトリごとにパッケージとしてまとめられる
Goではプロジェクト内にディレクトリを作成し、その中に.go
ファイルを配置すると、別パッケージとして扱えるようになります。(同じディレクトリに複数パッケージのソースコードを配置することはできない)
また、同じパッケージのソースコード同士では、importを書かなくても別ファイルの内容を参照できます。
例えばこんな感じのディレクトリ構成になっていたとして、
.
├── main.go
└── nice
├── bar.go
└── foo.go
nice/foo.go
とnice/bar.go
の一番上にはpackage nice
と書いておきます。
そうするとmain.go
内では、nice
パッケージをimportすることで、nice
パッケージで定義した型や関数をnice.HogeHoge
といった形で参照できるようになります。
また、nice/foo.go
とnice/bar.go
の中では、同じパッケージ内なので、内容を相互に参照可能です。
割と分かりやすいので、コードの整理をしやすそうだな〜という雰囲気を感じます。
以上です。