...

最初の Go

はじめに

このドキュメントはシンプルな Go パッケージの開発方法を紹介し, go tool を使い,Go パッケージとコマンドを取得,ビルド,インストールする標準的な方法を説明します。

go ツールを用いるには,コードを特定の方法で管理することが求められます。 どうぞこのドキュメントを注意深くお読みください。 Go をインストールして開発を始める最も簡単な方法を説明します。

動画で同様の説明をご覧いただけます。

コード構成

概要

各プロジェクトが異なったワークスペースを持ち,ワークスペースとバージョンコントロールレポジトリとが密接に結びついている他のプログラミング環境とは異なることに注意してください。

ワークスペース

ワークスペースとは,ディレクトリの階層構造で,そのルートには 2 つのディレクトリがあります。

go ツールはビルドすると,バイナリをbin ディレクトリにインストールします。

src のサブディレクトリにはたいてい,複数の (Git や Mercurial といった) バージョンコントロールレポジトリがあります。1 つのレポジトリが,1 つあるいはそれ以上のソースパッケージの開発を記録します。

実際のワークスペースがどのようになるか想像できるよう,例を示します。

bin/
    hello                          # コマンド実行ファイル
    outyet                         # コマンド実行ファイル
src/
    github.com/golang/example/
        .git/                      # Git レポジトリメタデータ
	hello/
	    hello.go               # コマンドソース
	outyet/
	    main.go                # コマンドソース
	    main_test.go           # テストソース
	stringutil/
	    reverse.go             # パッケージソース
	    reverse_test.go        # テストソース
    golang.org/x/image/
        .git/                      # Git レポジトリメタデータ
	bmp/
	    reader.go              # パッケージソース
	    writer.go              # パッケージソース
    ... (さらにたくさんのレポジトリとパッケージ) ...

上のフォルダ構成は,2 つのレポジトリ (exampleimage) をもつワークスペースとなっています。 example レポジトリは 2 つのコマンド (hellooutyet),そして 1 つのライブラリ (stringutil) があります。 image レポジトリには bmpほかにもいくつか パッケージがあります。

典型的なワークスペースはたくさんのパッケージとコマンドを含むソースレポジトリを複数含んでいます。 ほとんどの Go プログラマは すべての Go ソースコドと依存パッケージを 1 つのワークスペースの中に置いています。

シンボリックリンクを使ってファイルやディレクトリをワークスペースにリンクすべきではありません

コマンドとライブラリは異なる種類のソースパッケージを用いて構築します。 この違いについては,後ほど考慮します。

GOPATH 環境変数

GOPATH 環境変数でワークスペースの場所を指定します。 デフォルトでは,ホームディレクトリの中の go ディレクトリを指します。 つまり,Unix では $HOME/go, Plan 9 では $home/go , Windows では %USERPROFILE%\go (通常 C:\Users\YourName\go) となります。

異なる場所で作業したい場合, そのディレクトリへのパスを GOPATH に設定 します。 なお,(GOPATH=$HOME とするのも一般的です。) GOPATH は Go のインストールパスと同じにしてはいけません

コマンド go env GOPATH は現在有効な GOPATH を表示します。 環境変数が設定されていない場合,デフォルトの場所を表示します。

ワークスペースの bin サブディレクトリを PATH に加えると便利です。

$ export PATH=$PATH:$(go env GOPATH)/bin

表記を簡潔にするため,これ以降のスクリプトでは $(go env GOPATH) のかわりに $GOPATH を用います。 スクリプトを実行するとき, GOPATH を設定していないなら, $HOME/go コマンドを使うことができます。 あるいは,以下を実行してください。

$ export GOPATH=$(go env GOPATH)

GOPATH 環境変数について詳しくは, 'go help gopath' をご覧ください。

ワーススペースの場所をカスタマイズするには, GOPATH 環境変数を設定します。

インポートパス

インポートパス とは,パッケージを識別する文字列です。 パッケージのインポートパスはワークスペースあるいはリモートレポジトリの場所に対応しています。

標準ライブラリのパッケージには短いインポートパスがあります。 "fmt""net/http" のようにです。 自分のパッケージの場合,将来の標準ライブラいや外部ライブライと衝突ないであろうベースパスを選んでください。

自分のコードをソースレポジトリに保管する場合, そのソースレポジトリのルートをベースパスとします。 例えば, GitHub アカウントを持っているなら, github.com/user がベースパスとなります。

リモートレポジトリに置いて公開しなければビルドできないわけではありません。 もちろん,いつ公開しても良いようにコードを書くのは良い習慣です。 実際のところ,標準ライブラリや Go エコシステムとは異なるパスであれば,どんなパス名でも用いることができます。

github.com/user をベースパスとしましょう。 ワークスペースにソースコードを入れるディレクトリを作成します。

$ mkdir -p $GOPATH/src/github.com/user

最初のプログラムを書く

一つのプログラムをコンパイルして実行するには,まずパッケージパスを選びます。 (ここでは, github.com/user/hello を使います)。 そして,ワークスペース中に対応するパッケージディレクトリを作成します。

$ mkdir $GOPATH/src/github.com/user/hello

次に,そのディレクトリに hello.go というファイルを作成し, 以下の Go コードを書きます。

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}

では,このプログラムを go ツールを使ってビルドしインストールしてみましょう。

$ go install github.com/user/hello

このコマンドはシステムのどこからでも実行できます。 go ツールは GOPATH で指定されているワークスペース中にある github.com/user/hello パッケージを探し,ソースコードを見つけます。

パッケージディレクトリから go install を実行すれば,パッケージパスを省略できます。

$ cd $GOPATH/src/github.com/user/hello
$ go install

このコマンドは hello コマンドビルドし,実行バイナリを作成します。 そして,ワークスペースの bin ディレクトリに hello (Windows の場合は hello.exe) としてバイナリを配置します。 この例では,$GOPATH/bin/hello つまり $HOME/go/bin/hello というバイナリが作成されます。

go ツールはエラーが生じた時のみ出力があります。 出力がないなら,実行が成功したことを意味します。

コマンドラインからフルパスを入力して,プログラムを実行できます。

$ $GOPATH/bin/hello
Hello, world.

あるいは,$GOPATH/binPATH に追加してあるなら, バイナリ名を入力するだけで実行できます。

$ hello
Hello, world.

ソース管理システムを使っているなら,レポジトリを初期化し,ファイルを追加して,最初の変更をコミットしましょう。 もちろん,これは必須ではありません。 Go を書くのにソース管理は必要ではありません。

$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git repository in /home/user/go/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 7 insertion(+)
 create mode 100644 hello.go

コードをリモートレポジトリにプッシュするのは練習問題としておきます。

最初のライブラリを書く

では,ライブラリを作成して,hello プログラムから使って見ましょう。

ここでも,まずはパッケージパスを選び (ここでは github.com/user/stringutil とします),パッケージディレクトリを作成します。

$ mkdir $GOPATH/src/github.com/user/stringutil

次に,このディレクトリに reverse.go というファイルを作成し,以下を書き込みます。

// stringutil パッケージには文字列のユーティリティ関数があります。
package stringutil

// Reverse は引数の文字列を 1 文字ずつ左から右へ逆にします。
func Reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

では,go build でコンパイルできるか試してみましょう。

$ go build github.com/user/stringutil

あるいは,パッケージのソースディレクトリで作業しているなら,単に次のコマンドでもOKです。

$ go build

このコマンドを実行しても,ファイルは出力されません。 かわりに,コンパイルされたパッケージをローカルビルドキャッシュに保存します。

stringutil パッケージをビルドしたあと, ($GOPATH/src/github.com/user/hello にある) hello.go を変更して,このライブラリを使ってみましょう。

package main

import (
	"fmt"

	"github.com/user/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("!oG ,olleH"))
}

hello プログラムをインストールします。

$ go install github.com/user/hello

プログラムの新しいバージョンを実行すると,以下の逆になったメッセージが出力されます。

$ hello
Hello, Go!

以上のステップを行ったなら,ワークスペースは以下のようになっているはずです。

bin/
    hello                 # コマンド実行ファイル
src/
    github.com/user/
        hello/
            hello.go      # コマンドソース
        stringutil/
            reverse.go    # パッケージソース

パッケージ名

Go ソースファイルの最初のステートメントは,

package name

でなければなりません。ここで, name はインポートで使うパッケージのデフォルト名です。 (パッケージの中の全てのファイルは同じ name を使います)。

慣例的に,パッケージ名はインポートパスの最後の要素となります。 "crypto/rot13" というインポートパスのパッケージでは, パッケージ名は rot13 となります。

実行コマンドは常に package main を使います。

1 つのバイナリにリンクスされるパッケージ名がユニークである必要はありません。 インポートパス (フルファイル名) がユニークである必要があります。

Go の名前付けの慣例について詳しくは, Go の良い書き方 をご覧ください。

テスト

Go には go test コマンドと testing パッケージからなる軽量のテストフレームワークがあります。

ファイル名が _test.go で終わるファイルを作成し, func (t *testing.T) という形の TestXXX という名前の関数を作ります。 テストフレームワークはこのような関数を実行します。 関数が t.Errort.Fail のような失敗関数を呼び出したなら,テストは失敗となります。

stringutil パッケージにテストを追加しましょう。$GOPATH/src/github.com/user/stringutil/reverse_test.go というファイルを作成し,以下のコードを含めます。

package stringutil

import "testing"

func TestReverse(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"Hello, world", "dlrow ,olleH"},
		{"Hello, 世界", "界世 ,olleH"},
		{"", ""},
	}
	for _, c := range cases {
		got := Reverse(c.in)
		if got != c.want {
			t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

go test を実行してテストを走らせます。

$ go test github.com/user/stringutil
ok  	github.com/user/stringutil 0.165s

いつものように, go ツールをパッケージディレクトリから実行しているなら,パッケージパスを省略できます。

$ go test
ok  	github.com/user/stringutil 0.165s

詳細は, go help test を実行するか, testing パッケージドキュメント をご覧ください。

リモートパッケージ

インポートパスは, Git や Mercurial のようなレビジョン管理システムからパッケージソースコードを取得するためにも使えます。 go ツールはリモートレポジトリから自動的にパッケージを取得します。 例えば,このドキュメントで使った例は GitHub レポジトリ github.com/golang/example にもホストされています。 このレポジトリ URL をパッケージのインポートパスに含めれば, go get は自動的にこのパッケージを取得,ビルド,インストールします。

$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

ワークスペース中にパッケージが存在しないなら, go getGOPATH で指定された最初のワークスペースの中に配置します。 (パッケージがすでに存在するなら, go get はリモート取得をとばし, go install と同じように動作します。)

go get コマンドを実行したあと,ワークスペースディレクトリは 次のような階層構造になっているはずです。

bin/
    hello                           # コマンド実行ファイル
src/
    github.com/golang/example/
	.git/                       # Git レポジトリメタデータ
        hello/
            hello.go                # コマンドソース
        stringutil/
            reverse.go              # パッケージソース
            reverse_test.go         # テストソース
    github.com/user/
        hello/
            hello.go                # コマンドソース
        stringutil/
            reverse.go              # パッケージソース
            reverse_test.go         # テストソース

Github でホストされている hello コマンドは同じレポジトリの stringutil パッケージに依存しています。 hello.go ファイルの中のインポートは同じインポートパスを使っているので, go get コマンドは依存パッケージを見つけ,インストールできます。

import "github.com/golang/example/stringutil"

この慣例にしたがうなら,他の人があなたの Go パッケージを簡単に使うことができます。 Go Wikigodoc.org は外部の Go プロジェクトをリストしています。

go ツールを使ってリモートレポジトリを使う点での詳細は go help importpath をご覧ください。

さらに学ぶには

golang-announce メーリングリストを購読しましょう。Go の安定バージョンが出た際に知らせを受け取ることができます。

クリアで慣用的な Go コードを書くためのヒントが, Go の良い書き方 にあります。

Go ツアー に出かけ,Go 言語を正しく学びましょう。

Go 言語,ライブラリ,ツールについての詳細な記事を ドキュメントページ で見つけることができます。

困った時は

Freenode IRC サーバー上の #go-nuts にいる優しい Go プログラマに助けを求めることができます。

Go 言語のディスカッション用公式メーリングリストは Go Nuts です。

Go issue tracker でバグを報告してください。