はじめてのGo―シンプルな言語仕様、型システム、並行処理

第1章Go言語の特徴と環境構築―Googleが作った新言語

特集のはじめに

Goは、2009年にGoogleにより発表されたオープンソースのプログラミング言語です。C言語の開発者Ken Thompson、UTF-8の開発者Rob Pike、memcachedの開発者Brad Fitzpatrickといった名だたるエンジニアによって開発されています。

Goはシンプルな言語仕様であるため学習が比較的容易で、豊富な標準パッケージが同梱されているためすばやく目的を達成できます。また、巨大なコードでも高速にコンパイルできるため大規模開発にも適しており、Windows、OS X、Linuxなどの環境に合わせた実行ファイルを生成するクロスコンパイルのしくみがあるため作成したプログラムを容易に配布できます。並行処理のサポートも充実しており、ミドルウェアの開発などにも適しているとされています。

本特集では、2014年6月にリリースされた最新バージョンであるGo 1.3をベースに、本章でGoの特徴や環境構築、2章で基本文法、3章でGoでの型の考え方、4章で代表的な標準パッケージ、5章では並行処理のノウハウについて徹底解説します。

なお、本特集のサンプルコードは、WEB+DB PRESS Vol.82のサポートサイトから入手できます。

言語の特徴

Goは、CやC++などが使用されるシステムプログラミングの領域で、より効率良くプログラムを書くことを目的に作られました。巨大なコードベースのプロダクトを、多人数の開発者によって開発・メンテナンスするような場面で、品質を保つための工夫が随所に施されています。

以降ではGoの基本的な特徴を紹介します。

言語をシンプルに保つ

Goは、ほかの言語が持つような機能の多くを削り、言語をシンプルに保っています。こうした機能の排除は、コンパイルの高速化や、予期せぬミスを減らすことに役立っています。

最小限の構文

Goでは、繰り返し構文はfor文しかなく、while文やdo/while文などはサポートされていません。また条件分岐でも、ifの波括弧は省略できず、三項演算子もありません。こうした表現方法を制限することで、開発者による表現のばらつきを抑えることができます。

また、マクロのようなプリプロセスが必要な構文もサポートされていません。Goは巨大なコードベースも高速にコンパイルできるようにするため、コンパイルを遅くする原因となるこうした構文は極力取り込まない方針を採っています。

危険の回避

メモリリークの原因となりやすいポインタ演算や、意図せぬエラーの原因になる暗黙の型変換などは排除されています。

また、宣言されたものの使われていない変数や、インポートされたものの使われていないパッケージなどがコードに存在すると、コンパイルすら通りません。巨大なコードベースでは、コードを追加することよりもコードを削除するほうが、影響範囲が読みにくく難しいことがあります。そこでGoは不要なコードの存在をコンパイル時に検出することで、不要なコードが存在することを防いでいます。

例外の排除

RubyやJavaなど例外をサポートする言語では、関数呼び出しをtry/catch構文などで囲み、内部で発生した異常を例外として捕捉しますが、Goでは関数が多値を返せることを利用して、発生した異常を戻り値として呼び出し側に返す方針を採ります。また、ゼロ除算や配列の範囲外の添字へのアクセスなどでは、パニックとリカバという異常を処理するしくみを別途備えています。詳しくは2章で解説します。

特徴的な機能

Goは言語をシンプルに保つ一方で、近年のプログラミングに求められるモダンな機能は積極的にサポートしています。

クロスコンパイルのサポート

Goは、コンパイル時にOSとCPUアーキテクチャを指定し、その環境に合わせた実行ファイルを生成できます。詳細は本章のコラムで解説します。

並行処理のサポート

Goでは、ゴルーチンという軽量スレッドを用いて処理を並行に実施し、同時に実行されているゴルーチンの間ではチャネルという機能でデータをやりとりするしくみが備わっています。詳細は5章で解説します。

充実した開発環境

Goでは開発を助けるさまざまなパッケージやツールが最初から用意されており、さらに各エディタやツールのためのプラグインなども公開されているため、すぐに開発を始めることができます。

標準パッケージ

Goは標準パッケージが充実しており、外部のパッケージに頼らずともかなりの開発をカバーできます表1⁠。

標準パッケージの一覧は公式ドキュメントから確認できます。

表1 Goの主な標準パッケージ
パッケージ用途
archive、compresstar、zlibなどの圧縮やアーカイブ形式のサポート
cryptoAES、RSAなどの暗号化形式のサポート
databaseRDBへのSQLクエリインタフェースとドライバインタフェース
encodingJSON、XML、CSVなどのフォーマットのサポート
goGo言語自体のパーサやAST(抽象構文木)の取得
html、textHTMLやTEXT形式のテンプレート
netTCP、HTTP、RPC、SMTPなどのネットワークプログラミングのサポート
sync並行処理のためのロックや同期化のサポート
testingテストやベンチマークのサポート

ツールのサポート

Goには開発を支援するコマンドが同梱されており、さまざまなツールを使用できます表2⁠。

コマンドの解説は公式ドキュメントから確認できます。

表2 Goの主なコマンドラインツール
コマンド用途
go buildプログラムのビルド
go fmtGoの規約に合わせてプログラムを整形
go get外部パッケージの取得
go installプログラムのビルドとインストール
go runプログラムのビルドと実行
go testテストやベンチマークの実行
go tool yaccパーサをGoで出力するGo実装のyacc(パーサジェネレータ)
godocソースからドキュメントの生成

各エディタのサポート

Goでは、有志による主要なエディタやツールへのプラグインも公開されています[1]⁠。公開されているエディタやプラグインはGoのWikiにまとめられています。

採用事例

GoのWikiには、Goが採用されたプロジェクトの一部が掲載されています。最近はDevOps界隈での採用が目立ちますが、並行性を活かしたミドルウェアの開発や、クロスコンパイルによる配布容易性を活かしたコマンドラインツールなどにも採用されています表3⁠。

表3 Goの主な採用事例
採用事例説明
BazookaSoundCloudで使われている社内PaaS(Platform as a Service ⁠⁠。
https://developers.soundcloud.com/blog/go-at-soundcloud
Dockerコンテナ型の仮想環境ツール。起動が早くポータビリティも高いためImmutable Infrastractureなどの流れと相まって急速に普及しつつある。
https://www.docker.com/
hkHerokuをコマンドラインから操作するためのツール。
https://github.com/heroku/hk
mackerelはてなが提供するサーバ管理サービス。このエージェント部分にGoが採用されている。
https://mackerel.io/
Packer仮想環境へのOSインストールや、テンプレート作成を行うためのツール。Vagrantの作者としても有名なMitchellHashimotoにより開発された。
http://www.packer.io/
pecoシェル用のフィルタリングツールであるpercolのGo実装。
https://github.com/peco/peco
VitessGoogleがYouTubeのMySQLサーバをスケールさせるために開発したGo実装のミドルウェア。
https://github.com/youtube/vitess

インストール

Goは、公式に配布されている各環境に合わせたインストーラを用いて簡単にインストールできます。インストーラはダウンロードページから入手できます。お使いの環境に合わせてダウンロードし、実行してください。

インストールが完了したら、コマンドラインから次のコマンドを実行し、バージョンが出力されれば成功です。

$ go version
go version go1.3 darwin/amd64

hello world

まずはhello worldを作成して実行してみましょう。

作成

次の内容でhello.goというファイルを作成してください。文法については2章で解説するので、本章ではわからずとも問題ありません。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello world")
}

実行

作成したプログラムをすぐに実行したい場合は、次のようにgo runコマンドを使用します。

$ go run hello.go
hello world

hello worldが出力されれば成功です。

コンパイル

次にこのプログラムをコンパイルして、できあがったファイルを実行してみます。コンパイルには、go buildコマンドを使用します。

$ go build hello.go
$ ls
hello hello.go
$ ./hello
hello world

hello.goをコンパイルすると、hello(Windowsの場合はhello.exe)という実行形式のバイナリファイルが生成されます。ここでは64ビットのOS Xを用いてコンパイルしたため、生成された実行ファイルは、同じ64ビットのOS XであればGoがインストールされていなくても実行できます。

フォーマット

Goでは、標準のコーディング規約が定められています。これにより、プロジェクトごとにコーディング規約を用意したり、開発者同士の嗜好(しこう)を巡って無駄な言い争いが起こることを防いでいます。たとえばGoでは、ソフトタブ(半角スペース)ではなくハードタブ(タブ文字)でインデントすることや、半角スペースを入れるべき場所/入れるべきでない場所などを規約として定めています。

このコーディング規約はわざわざ覚える必要はありません。go fmtコマンドを用いると、規約に従ってコードを自動で整形できます。

$ go fmt hello.go

開発の際は、エディタでの保存時やバージョン管理システムへのコミット時などにgo fmtが実行されるようにし、常にフォーマット済みな状態にすることを心がけましょう。

ドキュメント

標準パッケージやサードパーティパッケージのドキュメントを確認するには、godocコマンドを使用します。たとえばhello.goで使用したfmtパッケージのドキュメントが見たければ、次のように実行します。

$ godoc fmt

ブラウザから見る公式サイトと同様のインタフェースでドキュメントを確認したい場合は、-httpオプションを付けて実行します。

$ godoc -http=":3000"

サーバが起動し、http://localhost:3000/にアクセスするとドキュメントを見ることができます。

Goのプロジェクト構成とパッケージ

先ほどは1つのファイルにプログラムを記述しましたが、ファイルが増えたときは、パッケージを分割する場合があります。その場合は、プロジェクトをきちんと構成し環境変数を指定する必要があります。

ここでは、myprojectというプロジェクトの中でgosampleというパッケージを作成し、そのパッケージをmainパッケージから呼び出すよう構成してみます。

ディレクトリの作成

まず、myprojectディレクトリを起点に、次のようにbin、pkg、srcという3つのディレクトリを作成します。

$ tree myproject
myproject
├── bin # go install時の格納先
├── pkg # 依存パッケージのオブジェクトファイル
└── src # プログラムのソースコード

環境変数GOPATHの指定

次に、myprojectディレクトリのパスをGOPATHという環境変数に指定します。

$ cd myproject
$ export GOPATH=`pwd` # myprojectをGOPATHに登録

Goのコマンドは、このGOPATHとその下にある先の3つのディレクトリの命名規則を用いて、Makefileなどの構成ファイルは一切なしで、依存関係を解決してビルドできるようになっています。

パッケージの作成

そしてパッケージを作ります。Goでは、1つのパッケージは1つのディレクトリに格納します。

gosampleパッケージ

まずgosampleパッケージを作ります。src/gosample/gosample.goを次のように作成します。

package gosample

var Message string = "hello world"

上記ではgosampleパッケージの中に、Messageという変数を定義しています。

mainパッケージ

次にmainパッケージを作ります。src/main/main.goを次のように作成します。

package main
import (
    "fmt"
    "gosample"
)

func main() {
    fmt.Println(gosample.Message) // hello world
}

上記ではmain パッケージのmain()の中で、gosample パッケージを使用しています。このgosampleパッケージのインポートは、先ほど指定したGOPATHから、$GOPATH/src/gosampleと解決され実行されます。

ビルドと実行

実行

正しくGOPATHが設定された状態でgo runコマンドでmain.goを実行すると、gosampleパッケージの場所が正しく解決され、プログラムを実行できます。

$ cd $GOPATH/src/main
$ go run main.go
hello world

ビルド

次にこのプログラムをビルドして、1つの実行形式のファイルを生成してみます。go buildコマンドを用いてその場に実行ファイルを作ることもできますが、go installコマンドを用いると、生成されたファイルが$GOPATH/binに自動的に格納されます。

$ cd $GOPATH/src/main
$ go install

ビルドしたあとのプロジェクト内は次のようになります。

$ tree myproject
myproject
├── bin
│ └── main
├── pkg
│ └── darwin_amd64
│ └── gosample.a
└── src
    └── gosample
    |  └── gosample.go
    └── main
        └── main.go

このようにbinディレクトリに実行ファイルが生成されるため、次のように$GOPATH/binをパスに追加しておけば、go installしたコマンドに常にパスを通すことができます。

$ export PATH=$PATH:$GOPATH/bin
$ main
hello world

パッケージの公開

作成したパッケージを公開してみましょう。といってもGo には、Ruby のRubyGems やNode.js のnpmNode Package Managerのような標準のパッケージ管理システムはありません。代わりに、GitやMercurialでリポジトリが公開されていれば使用できます。専用の設定ファイルなども必要ありません。

先ほどのgosampleパッケージをGitHubで公開してみます。まずは空のリポジトリでよいので自分のGitHubにリポジトリを用意してください。ここではhttps://github.com/wdpress/gosampleにリポジトリを作成し、公開することにします。リポジトリのURLやディレクトリ名は、自分で作成したものに置き換えてください。

ディレクトリ構造の修正

このリポジトリに合わせてディレクトリ構造を修正します。

$ tree myproject
myproject
├── bin
├── pkg
└── src
    └── github.com
    |  └── wdpress
    |      └── gosample
    |          └── gosample.go
    └── main
        └── main.go

mainパッケージの修正

パッケージを使うmain.goも修正します。

package main

import (
    "fmt"
    "github.com/wdpress/gosample"
)

func main() {
    fmt.Println(gosample.Message) // hello world
}

GitHubに公開

あとは、gosampleディレクトリ以下をGitで管理し、GitHubにpushするだけです。

$ cd $GOPATH/src/github.com/wdpress/gosample
$ git init
$ git add .
$ git commit -m 'my first golang package'
$ git remote add origin https://github.com/wdpress/gosample
$ git push origin master

これで公開が完了です。

公開したパッケージを使ってみる

では、試しに自分で取得して使ってみましょう。

ローカルにあるパッケージを削除し、GitHubから取得します。取得にはgo getコマンドを使います。

$ cd $GOPATH/src
$ rm -rf github.com
$ go get github.com/wdpress/gosample

取得したパッケージは、go getで指定したパスと同じディレクトリ構成でプロジェクト内に展開されます。

再度main.goをビルドして実行し、正しく動作すれば成功です。

GOPATHを固定する

複数のプロジェクトがマシン内の別の個所にある場合は、プロジェクトを移動するたびにGOPATHを指定しなおす必要があります。その面倒を避けるため最近では、マシン内に1ヵ所だけGOPATHを指定し、すべてのパッケージをそこで管理し、開発もそこで行う方法が主流になってきました。これからGoを始める場合は、まずこの方法を試してみることをお勧めします。

まとめ

本章では、Goの特徴と環境構築方法について解説しました。いよいよ次章以降では、Goでのプログラミングについてより細かく解説していきます。

おすすめ記事

記事・ニュース一覧