Goのコードの書き方
はじめに
このドキュメントでは、モジュール内のシンプルなGoパッケージの開発を実演し、Goモジュール、パッケージ、コマンドの取得、ビルド、インストールを行う標準的な方法であるgoツールを紹介します。
コードの構成
Goプログラムはパッケージに編成されます。パッケージとは、同じディレクトリにある、一緒にコンパイルされるソースファイルの集まりのことです。1つのソースファイルで定義された関数、型、変数、定数は、同じパッケージ内の他のすべてのソースファイルから参照できます。
リポジトリには、1つ以上のモジュールが含まれています。モジュールとは、一緒になってリリースされる関連するGoパッケージの集まりです。Goリポジトリには通常、リポジトリのルートにある1つのモジュールのみが含まれています。そこにあるgo.mod
という名前のファイルは、モジュールパスを宣言します。これは、モジュール内のすべてのパッケージのインポートパスのプレフィックスです。モジュールには、そのgo.mod
ファイルを含むディレクトリ内のパッケージと、そのディレクトリのサブディレクトリ(次の別のgo.mod
ファイルを含むサブディレクトリまで(存在する場合))が含まれます。
コードをビルドする前に、リモートリポジトリに公開する必要はないことに注意してください。モジュールは、リポジトリに属することなくローカルで定義できます。ただし、いつか公開するつもりでコードを整理するのは良い習慣です。
各モジュールのパスは、パッケージのインポートパスのプレフィックスとして機能するだけでなく、go
コマンドがダウンロードする場所を示すものでもあります。たとえば、モジュールgolang.org/x/tools
をダウンロードするために、go
コマンドはhttps://go.dokyumento.jp/x/tools
で示されるリポジトリを参照します(詳しくはこちらを参照してください)。
インポートパスとは、パッケージをインポートするために使用される文字列です。パッケージのインポートパスは、モジュールパスとそのモジュール内のサブディレクトリを結合したものです。たとえば、モジュールgithub.com/google/go-cmp
には、cmp/
ディレクトリにパッケージが含まれています。そのパッケージのインポートパスはgithub.com/google/go-cmp/cmp
です。標準ライブラリのパッケージには、モジュールパスのプレフィックスはありません。
最初のプログラム
簡単なプログラムをコンパイルして実行するには、まずモジュールパス(example/user/hello
を使用します)を選択し、それを宣言するgo.mod
ファイルを作成します。
$ mkdir hello # Alternatively, clone it if it already exists in version control. $ cd hello $ go mod init example/user/hello go: creating new go.mod: module example/user/hello $ cat go.mod module example/user/hello go 1.16 $
Goソースファイルの最初のステートメントは、package name
である必要があります。実行可能コマンドは常にpackage main
を使用する必要があります。
次に、そのディレクトリ内に、次のGoコードを含むhello.go
という名前のファイルを作成します。
package main import "fmt" func main() { fmt.Println("Hello, world.") }
これで、go
ツールを使用してそのプログラムをビルドおよびインストールできます。
$ go install example/user/hello $
このコマンドは、hello
コマンドをビルドし、実行可能なバイナリを生成します。次に、そのバイナリを$HOME/go/bin/hello
(または、Windowsの場合は%USERPROFILE%\go\bin\hello.exe
)としてインストールします。
インストールディレクトリは、GOPATH
とGOBIN
の環境変数によって制御されます。GOBIN
が設定されている場合、バイナリはそのディレクトリにインストールされます。GOPATH
が設定されている場合、バイナリはGOPATH
リストの最初のディレクトリのbin
サブディレクトリにインストールされます。それ以外の場合、バイナリはデフォルトのGOPATH
($HOME/go
または%USERPROFILE%\go
)のbin
サブディレクトリにインストールされます。
go env
コマンドを使用して、今後のgo
コマンドの環境変数のデフォルト値をポータブルに設定できます。
$ go env -w GOBIN=/somewhere/else/bin $
go env -w
で以前に設定された変数を設定解除するには、go env -u
を使用します。
$ go env -u GOBIN $
go install
などのコマンドは、現在の作業ディレクトリを含むモジュールのコンテキスト内で適用されます。作業ディレクトリがexample/user/hello
モジュール内にない場合、go install
は失敗する可能性があります。
便宜上、go
コマンドは作業ディレクトリからの相対パスを受け入れ、他のパスが指定されていない場合は、現在の作業ディレクトリのパッケージをデフォルトにします。したがって、作業ディレクトリでは、次のコマンドはすべて同等です。
$ go install example/user/hello
$ go install .
$ go install
次に、プログラムが正常に動作することを確認するために、プログラムを実行しましょう。さらに便利にするために、バイナリの実行を簡単にするために、インストールディレクトリをPATH
に追加します。
# Windows users should consult /wiki/SettingGOPATH # for setting %PATH%. $ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .)) $ hello Hello, world. $
ソース管理システムを使用している場合は、リポジトリを初期化し、ファイルを追加して、最初の変更をコミットするのが良いでしょう。繰り返しますが、この手順はオプションです。Goコードを作成するためにソース管理を使用する必要はありません。
$ git init Initialized empty Git repository in /home/user/hello/.git/ $ git add go.mod hello.go $ git commit -m "initial commit" [master (root-commit) 0b4507d] initial commit 1 file changed, 7 insertion(+) create mode 100644 go.mod hello.go $
go
コマンドは、対応するHTTPS URLをリクエストし、HTMLレスポンスに埋め込まれたメタデータを読み取ることで、指定されたモジュールパスを含むリポジトリを見つけます(go help importpath
を参照)。多くのホスティングサービスは、Goコードを含むリポジトリに対してすでにそのメタデータを提供しているため、モジュールを他の人が使用できるようにする最も簡単な方法は、通常、そのモジュールパスをリポジトリのURLと一致させることです。
モジュールからパッケージをインポートする
morestrings
パッケージを作成し、hello
プログラムから使用しましょう。まず、$HOME/hello/morestrings
という名前のパッケージのディレクトリを作成し、次に、そのディレクトリに次の内容のreverse.go
という名前のファイルを作成します。
// Package morestrings implements additional functions to manipulate UTF-8 // encoded strings, beyond what is provided in the standard "strings" package. package morestrings // ReverseRunes returns its argument string reversed rune-wise left to right. func ReverseRunes(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) }
ReverseRunes
関数が大文字で始まるため、エクスポートされており、morestrings
パッケージをインポートする他のパッケージで使用できます。
go build
でパッケージがコンパイルされることをテストしましょう。
$ cd $HOME/hello/morestrings $ go build $
これにより、出力ファイルは生成されません。代わりに、コンパイルされたパッケージがローカルビルドキャッシュに保存されます。
morestrings
パッケージがビルドされることを確認したら、hello
プログラムから使用しましょう。これを行うには、元の$HOME/hello/hello.go
を修正してmorestringsパッケージを使用します。
package main import ( "fmt" "example/user/hello/morestrings" ) func main() { fmt.Println(morestrings.ReverseRunes("!oG ,olleH")) }
hello
プログラムをインストールします。
$ go install example/user/hello
新しいバージョンのプログラムを実行すると、新しい反転されたメッセージが表示されます。
$ hello Hello, Go!
リモートモジュールからパッケージをインポートする
インポートパスは、GitやMercurialなどのリビジョン管理システムを使用してパッケージのソースコードを取得する方法を記述できます。go
ツールはこのプロパティを使用して、リモートリポジトリからパッケージを自動的にフェッチします。たとえば、プログラムでgithub.com/google/go-cmp/cmp
を使用するには
package main import ( "fmt" "example/user/hello/morestrings" "github.com/google/go-cmp/cmp" ) func main() { fmt.Println(morestrings.ReverseRunes("!oG ,olleH")) fmt.Println(cmp.Diff("Hello World", "Hello Go")) }
外部モジュールに依存するようになったので、そのモジュールをダウンロードし、そのバージョンをgo.mod
ファイルに記録する必要があります。go mod tidy
コマンドは、インポートされたパッケージに必要なモジュール要件を追加し、不要になったモジュールの要件を削除します。
$ go mod tidy go: finding module for package github.com/google/go-cmp/cmp go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4 $ go install example/user/hello $ hello Hello, Go! string( - "Hello World", + "Hello Go", ) $ cat go.mod module example/user/hello go 1.16 require github.com/google/go-cmp v0.5.4 $
モジュールの依存関係は、GOPATH
環境変数で示されるディレクトリのpkg/mod
サブディレクトリに自動的にダウンロードされます。モジュールの特定のバージョンのダウンロードされた内容は、そのバージョンをrequire
する他のすべてのモジュールで共有されるため、go
コマンドはそれらのファイルとディレクトリを読み取り専用としてマークします。ダウンロードされたすべてのモジュールを削除するには、go clean
に-modcache
フラグを渡します。
$ go clean -modcache $
テスト
Goには、go test
コマンドとtesting
パッケージで構成される軽量のテストフレームワークがあります。
func (t *testing.T)
シグネチャを持つTestXXX
という名前の関数を含む、_test.go
で終わる名前のファイルを作成することでテストを作成します。テストフレームワークは、そのような各関数を実行します。関数がt.Error
やt.Fail
などの失敗関数を呼び出すと、テストは失敗したとみなされます。
次のGoコードを含むファイル$HOME/hello/morestrings/reverse_test.go
を作成して、morestrings
パッケージにテストを追加します。
package morestrings import "testing" func TestReverseRunes(t *testing.T) { cases := []struct { in, want string }{ {"Hello, world", "dlrow ,olleH"}, {"Hello, 世界", "界世 ,olleH"}, {"", ""}, } for _, c := range cases { got := ReverseRunes(c.in) if got != c.want { t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want) } } }
次に、go test
でテストを実行します。
$ cd $HOME/hello/morestrings $ go test PASS ok example/user/hello/morestrings 0.165s $
詳細については、go help test
を実行し、testingパッケージのドキュメントを参照してください。
次は何をすべきか
Goの新しい安定バージョンがリリースされたときに通知を受け取るには、golang-announceメーリングリストを購読してください。
明確で慣用的なGoコードを作成するためのヒントについては、Effective Goを参照してください。
言語自体を学ぶには、Goツアーを受講してください。
Go言語とそのライブラリおよびツールに関する詳細な記事については、ドキュメントページにアクセスしてください。
ヘルプの入手
リアルタイムのヘルプが必要な場合は、コミュニティ運営のgophers Slackサーバーで親切なゴーファーに質問してください(招待状はこちらで入手してください)。
Go言語に関する議論のための公式メーリングリストは、Go Nutsです。
バグの報告は、Go issue trackerを使用してください。