(モジュールを使った) Go コードの書き方 (翻訳)

このドキュメントについて

このドキュメントはシンプルなモジュールを使った Go パッケージの開発を実演し、go ツールを紹介するためにあります。go ツールとは Go のモジュール・パッケージ・コマンドをフェッチ・ビルド・インストールする標準的な方法です。

注意: このドキュメントは Go 1.13 以降かつ GO111MODULE 環境変数がセットされていない環境を前提としている点に注意してください。モジュールを使わない以前のバージョンに関するドキュメントはここにあります。

Go プログラムの構成

Go のプログラムはパッケージと呼ばれる単位で管理されます。パッケージ (package) は特定のディレクトリ下に存在するソースファイルを集めたものであり、パッケージに含まれるソースファイルはコンパイルにおける一単位となります。例えばパッケージ内のとあるソースファイルで定義される関数・型・変数・定数は、同じパッケージ内の他の全てのソースファイルから参照可能です。

レポジトリには一つ以上のモジュールが含まれます。モジュールは Go パッケージを集めたものであり、リリースにおける一単位となります。たいていの Go レポジトリにはモジュールが一つだけ存在し、レポジトリのルートに置かれます。ルートの go.mod という名前のファイルがモジュールパスを宣言します。モジュールパス (module path) とは、モジュール内の全てのパッケージが利用するインポートパスのプレフィックス (接頭部) です。go.mod を持つディレクトリおよびそのサブディレクトリがそれぞれモジュールのパッケージとなりますが、サブディレクトリに go.mod が存在する場合にはそのパッケージは含まれません。

各モジュールのパスはパッケージのインポートパスのプレフィックスになるだけではなく、go コマンドがパッケージをダウンロードするときにも使われます。例えば golang.org/x/tools というモジュールをダウンロードするときには、go コマンドは https://golang.org/x/tools にあるレポジトリを探索します (詳細はこのページで説明されています)。

パッケージのインポートパス (import path) はパッケージをインポートするのに使われる文字列であり、モジュールパスとモジュールにおけるサブディレクトリを結合したものです。例えばモジュール github.com/google/go-cmp のディレクトリ cmp/ にあるパッケージを考えると、このパッケージのインポートパスは github.com/google/go-cmp/cmp となります。ただし標準ライブラリのパッケージはモジュールパスのプレフィックスを持ちません。

最初のプログラム

簡単なプログラムをコンパイルして実行するには、まず適当に選んだモジュールパス (ここでは example.com/user/hello) を go.mod ファイルに書き込みます:

$ mkdir hello # バージョン管理されているなら、それをクローンしてもよい
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello

go 1.14
$

Go のソースファイルの最初の文は package name である必要があり、実行可能なコマンドでは必ず package main でなければなりません。

hello.go というファイルを同じディレクトリを作り、次の Go コードを書き込みます:

package main

import "fmt"

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

このプログラムは go ツールを使ってビルド・インストールできます:

$ go install example.com/user/hello
$

このコマンドは hello コマンドをビルドし、実行可能バイナリを生成します。バイナリのインストール先は $HOME/go/bin/hello です (Windows ではインストール先が %USERPROFILE%\go\bin\hello.exe となります)。

バイナリがインストールされるディレクトリは環境変数 GOPATHGOBIN によって管理されます。GOBIN が設定されているならバイナリはそのディレクトリにインストールされ、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 -w GOBIN=/somewhere/else/bin
$

go install といったコマンドはカレントディレクトリにあるモジュールに対して動作します。そのため example.com/user/hello モジュールがカレントディレクトリにない場合、モジュールはインストールされません。

簡単のために、go コマンドはカレントディレクトリからの相対パスも受け付けます。さらにパスが与えられていなければカレントディレクトリをデフォルトで相対パスとして使うので、今の例では次の三つのコマンドが同じ動作をします:

$ go install example.com/user/hello
$ go install .
$ go install

続いてこのプログラムが正しく動くことを確認しましょう。まずインストールディレクトリを PATH に追加してバイナリを簡単に実行できるようにします:

# Windows での %PATH% の設定方法は https://github.com/golang/go/wiki/SettingGOPATH を参照
$ 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 コードを含むレポジトリのメタデータを提供するので、あなたのモジュールを他の人から利用可能にする一番簡単な方法は、モジュールパスをレポジトリの 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 プログラムから morestrings を使ってみましょう。$HOME/hello/hello.go を次のように変更します:

package main

import (
      "fmt"

      "example.com/user/hello/morestrings"
)

func main() {
      fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

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

$ go install example.com/user/hello

新しいバージョンのプログラムを実行すると、新しくなった逆のメッセージが表示されます:

$ hello
Hello, Go!

リモートのモジュールに含まれるパッケージをインポートする

インポートパスを使ってパッケージのソースコードを取得する方法を指定できます。ソースコードの取得には Git や Mercurial といったリビジョン管理システムが使われ、go ツールはインポートパスを使ってリモートレポジトリからパッケージを自動的に取得します。例えば github.com/google/go-cmp/cmp を今のプログラムで使うには、次のようにします:

package main

import (
      "fmt"

      "example.com/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 install, go build, go run といったコマンドを実行すると、go コマンドは自動的にリモートモジュールをダウンロードし、そのバージョンを go.mod ファイルに書き込みます:

$ go install example.com/user/hello
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.4.0
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.4.0
$ hello
Hello, Go!
  string(
-     "Hello World",
+     "Hello Go",
  )
$ cat go.mod
module example.com/user/hello

go 1.14

require github.com/google/go-cmp v0.4.0
$

自動的にダウンロードされる依存モジュールの保存先は環境変数 GOPATH が指すディレクトリの pkg/mod サブディレクトリです。あるバージョンのモジュールがダウンロードされると、そのバージョンを使う他の全てのモジュールもそのファイルを使うようになります。そのため go コマンドは pkg/mod フォルダを読み込み専用に設定します。go clean-modcache を渡すとダウンロードしたモジュールを全て削除できます:

$ go clean -modcache
$

テスト

Go には go test コマンドと testing パッケージを使った軽量テストフレームワークがあります。

テストは XXX_test.go という名前のファイルに書きます。func (t *testing.T) というシグネチャを持った TestXXX という名前の関数を定義すれば、go test が起動するテストフレームワークがその関数を実行します。TestXXXt.Errort.Fail といった失敗関数を呼ぶと、テストは失敗したものとみなされます。

パッケージにテストを追加するために $HOME/hello/morestrings/reverse_test.go というファイルを作成し、次の Go コードを書き込みます:

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 でテストを実行します:

$ go test
PASS
ok    example.com/user/morestrings 0.165s
$

詳細は go help test あるいは testing パッケージのドキュメント を参照してください。

Attribution

Portions of this page are reproduced from work created and shared by Google and used according to terms described in the Creative Commons 4.0 Attribution License.

The original source page is How to Write Go Code.

広告