OthloBlog - オスロブログ -

名古屋のIT系学生コミュニティOthloTechのブログです。

Golangことはじめ

f:id:Juju_62q:20180414003931j:plain

おはこんばんちは!@ジュジュです!
今回のブログでは近頃勢いがさらに加速している言語であるGolangについてかいていきたいと思います。
Gopherくん。かわいいですよね。ちなみにホリネズミらしいです。

対象読者

  • ほかの言語はやったことあるけどGolangに触ったことない人。
  • Golangになんとなく興味がある人。
  • Gopher君が好きな人。

記事の内容

  • Golangの基本的な特徴を抑えていきます。
  • Golangの基本的な文法に触れていきます。
  • Golangを書く上で利用したいツール。
  • Golangで気を付けておきたいこと。

この記事でやらないこと

  • 環境構築の方法等には触れません。
  • 制御構文等の文法には触れません。
  • goroutine, channelについてはほとんど触れません。

Goとは

GoはGoogleが開発したプログラミング言語です。ただし、文脈上でGoと言われても何のことだかわからないのでGolangとか日本語であればGo言語といわれることが多いです。検索の際にもそういった方式をとります。本記事では基本的にGolangと記述します。

Golangの特徴

Golangはコンパイルを行って命令コードを生成する静的型付け言語で、Haskell等関数型言語ほどではないですがそこそこの型システムとしての安全性を持っています。
コンパイル後のプログラムは高速に動作するほかGCやメモリ安全性も備えています。言語の特徴として並列処理が非常に得意(プログラムを作成しやすい)ということも実行のはやさに拍車をかけています。
また、後述しますがGolangは実装、実行の際にできるだけ少ない手数で、シンプルに開発ができるように作られています。
このような特徴からミドルウェアを作成するのに非常に適している言語であり、実際にDockerKubernetesDgraph等のミドルウェアがGolangによって記述されています。

Golangのシンプルさ

Golangには多くのモダンな言語が備えているジェネリクス、継承、例外等の機能が排除されています。このことから、ソースコードが必要以上に階層化せず、複雑になることを避けることができます。また、機能が制限されているがゆえにコーダーが変わってもほかの言語よりも実装のぶれは生まれにくい言語といえます。
ただ、僕としてはジェネリクスだけはマジでほしいです。。。

実装、実行の簡単さ

型推論(変数宣言、代入時のもの)

Golangは静的型付け言語ですが必要以上に型を指定することは要求しません。それどころかKotlinやC#などで利用するようなvarという宣言も不要です。

変数の初期化(代入の際には)

a := "hoge"

と行えばaが変数宣言されるとともに文字列型と判定されます。

パッケージのダウンロード方法

Golangでデータをパッケージをダウンロードする際には

go get github.com.xxxx

というようなコマンドを利用します。こうすることでインターネットからソースコードをダウンロードし、利用できるようになります。したがって無秩序にroot権限を利用してGolangの実行環境をインストールしていき環境が汚れていくというようなことが少ないです。さらに言えばパッケージのコードの見やすさ、修正のしやすさもピカ1だと思います。

テスト

Golangのテストファイルはxxx_test.goという命名規則で作成することになっています。このように命名したうえで

go test -run ""

とかをたたくだけでテストを実施できます。シェルをたたくだけなのでどんな環境でもできて、かつ手数も少なく実行できそうですよね。

上記以外にもGolangの特徴である並列処理を支えるgoroutine, channelというような機構がありますが、この辺りについて話すと少し記事が長くなるのでQiitaをご覧ください。

qiita.com

Golangの特徴的文法

エラー処理

例外の存在しないGolangでは返り値としてエラー処理します。
例えば以下のような感じで処理します。

bytes, err := ioutil.ReadFile("hoge.json")
if err != nil {
    log.Fatal(err)
}

この例ではファイルを読み込んでいるのですが、第一返り値にはファイルを読んだデータが入っていて第二返り値にはエラーが格納されています。戻ってきたエラーが空かどうかをチェックして、空でなければエラーを出力するような処理を行います。
この文に関してはGolangを使っている限りめちゃくちゃ書くことになるので煩わしいと思う方もいると思うのですが、プログラミングを始めたての時にやりがちなエラー処理でキャッチしないとかそういったことを比較的防ぐことができるため個人的には気に入っています。

なお、エラーを返すような関数を作成する際には

if err != nil {
    return errors.Wrap(err, "error occurs at xxx function")
}

といったようにエラーがどのような道を通って発生したのかわかるようにラップしてあげると親切です。

パブリック、プライベートの切り分け

個人的にこれ割と画期的だなと思ったのですがGolangではパブリック(プライベート)なメソッド等を作るためにわざわざpublicとか記述をすることはありません。先頭が大文字であればパブリックなものになり、小文字であればプライベートなものになります。記述文字数めっちゃ減りそうですよね。とはいえ自分は最初よく言語仕様を調べずに書いていたので認知してませんでした笑

internalディレクトリ

これは文法ではないですが、Golangのプログラムには唐突にinternalという一見意味のなさそうなディレクトリが出現します。これは同一パッケージ内からしか参照されないプログラムを示していて、外部から利用できないプログラム群を表しています。外部から操作されたくないプログラムに関してはinternalディレクトリの中に存在することが多いです。

Golangを書くときに利用したいツール

書き方を補正してくれるツール

書き方補助のデファクトになっているものとして下記の3つがあげられます。

  • go fmt
  • go imports
  • golint

go fmt

go fmt はGolangでコーディングするときにif文で条件式の後にスペースを入れるかとかそういったコーダーの癖を1つの形式に整えてくれるツールです。それ以外にもパッケージのインポートの順番等を基本的な方法に合わせてくれます。エディタに設定して保存時に自動で設定されるようにすると幸せになれます。vim-goを使うと最初から設定されています。下記に適用例を示します。

f:id:Juju_62q:20180414021715j:plain

go imports

Golangでは不必要なパッケージがimportされているとコンパイルエラーとなってしまいます。go importsを利用すると保存時に不必要なパッケージimportを削除してくれます。パッケージの追加も自動で行われるほか、Golangの標準パッケージとユーザパッケージを分けてimport文に記述するような気候も備えています。これもエディタに設定して保存時に自動で設定されるようにすると幸せになれます。vim-goを使うと最初から設定されています。

golint

golintはGolangで利用されるlintです。変数の命名方法に指定がある場合の注意や実装上届かないコード、go docに合わせてグローバルなメソッド及び変数に説明がない場合に注意をします。特に変数名は以下のようになり、実装者による振れ幅を削減できます。

Id -> ID
Url -> URL
Json -> JSON

パッケージマネージャ

現状のGolangには公式のパッケージマネージャは存在しません。(version 1.12からvgoが取り込まれるようです。)
とはいえgo getをしまくっているとどのコミットに対して行ったのかよくわからなくなるためこの辺りを管理するツールは必要です。 Golangのパッケージマネージャとしては

  • dep
  • glide

あたりが有名なものとして存在しますが、現在(2018/04)ではdepがよくつかわれているとおもっています。
depの使い方については

qiita.com

この辺りを参考にしていただければと思います。

Golangアンチパターン

初期化をした後基本的に変化しない変数について

DBへの接続文字列を環境変数から取得するなど変更の余地は残したいけど基本的に変更しないような変数についてはGolangでは基本的にグローバルにしたり、セッタメソッドを用意しません。これはGolangは並列処理が得意な言語でありどこでいつ変更されるのかが担保できず、Race Conditionが発生する場合があるためです。したがってこのような場合に値を変更して使う際には接続文字列を構造体等で返してからそのローカル変数の値を変更するような形になります。これは並列処理が得意なGolang独特のアンチパターンですね。

interface{}について

Golangではジェネリクス型がないために汎用的にコードを書く際、ついついinterface{}を利用してしまうということがあるかと思います。でもこれって考えてみるとmapや構造体でよかったりすることって割とあるかと思います。プログラムのバグを回避するためにもなるべく制限を与えるようなプログラムを書くようにしていきましょう!

おわりに

いかがだったでしょうか?Golangは役割が決まっているプログラムに対してはかなり使いやすい言語ではあるので本記事を読んでそのとっかかりを持っていただけたら嬉しいです。とはいえ、現在まだWAFやORマッパーのデファクトと呼べるものが存在しないなどコーダーにゆだねられる部分が多くもあると思っています。(RubyだったらRails一択)
もうすぐGoConもあるしどんどん知見の共有が行われて落ち着いてくることに期待しましょう!ところでそろそろGopher君のぬいぐるみを買おうかなと思っています。

f:id:Juju_62q:20180414021600j:plain それではみなさん、快適なハックライフを!