コードの書き方

ここでは小さなコードを書いて、実行、テストする方法を学びます。

このページはGo1.13以降の利用を前提とします。

コードの単位

コードを書き始める前に、コードの作成単位について説明します。

Goにはコードのまとまりとして、次の単位があります。

  • モジュール:パッケージの集合、依存関係を含むコードの配布・バージョン管理単位
  • パッケージ:同じディレクトリにあるソースファイルの集合、コードの利用単位
  • ソースファイル:同じパッケージの間で共有される型・変数・定数・関数の集合、ファイル単位

具体的な構成例として、この後に説明するサンプルの構成を次に示します。

モジュールパス:example.com/user/myapp

myappディレクトリ(パッケージ:main)
├── go.mod(モジュールのバージョンを管理するファイル)
├── go.sum(利用モジュールのsum値を管理するファイル)
├── myapp.go(mainパッケージのソースファイル)
└── calcディレクトリ(パッケージ:calc)
     ├── calc.go(calcパッケージのソースファイル)
     └── calc_test.go(calcパッケージのソースファイル)

モジュールのルートディレクトリはmyappディレクトリです。 このリポジトリはモジュールexample.com/user/myappを含み、モジュールはmainパッケージとcalcパッケージを含みます。

モジュールパスはインポートパスの接頭辞として機能します。 たとえば、calcパッケージのインポートパスはexample.com/user/myapp/calcです。 標準ライブラリのパッケージはインポートパスにモジュールパスを含みません。

さらにモジュールパスはモジュールの場所を表します。 一般的にモジュールはGitHubやGitLabなどのインターネット上のサービスに公開します。 そのサービスのURLをモジュールパスにすることで、インターネット上にあるモジュールを簡単に利用できます。 たとえば、GitHubではgithub.com/<user>/<repo>をモジュールパスとします。 もちろん、公開はしなくてもモジュールを作成できます。 サンプルのモジュールパスは実在しないためexample.com/user/myappとしています。

最初のプログラム

リポジトリを作成し、Goのコードを書いて、実行する方法を説明します。

最初にリポジトリ用のディレクトリとして、myappディレクトリを作成します。 それから、myappディレクトリに移動し、go mod initコマンドを実行することで現在のディレクトリがGoのモジュールであることを宣言します。

go mod initを実行すると現在のディレクトリにgo.modというファイルが作成されます。 ここにはモジュールに関する情報が記録されます。

mkdir $HOME/myapp
cd myapp
# 現在のディレクトリがモジュールであることを宣言する
go mod init example.com/user/myapp
# ファイルgo.modが作成されたことを確認する
ls

次にコードを書きます。現在のディレクトリにmyapp.goというソースファイルを作成します。ファイルは次の内容としてください。

package main

import (
    "fmt"
)

// mainパッケージのmain関数はプログラムの始まりとなる場所
func main() {
    /*
      画面に『Hello, world』と表示する
      これはコメントでプログラムの動作に影響しない
    */
    fmt.Println("Hello, world.")
}

go runコマンドを使ってコードをコンパイルし、実行します。 引数は実行対象のディレクトリを指定できます。 ここでは現在のディレクトリ(mainパッケージ)を実行するため.を指定します。

cd $HOME/myapp
go run .

画面に次の内容が表示されます。

Hello, world

go installコマンドを使ってコンパイルし、実行可能なバイナリファイルを出力できます。 デフォルトでは$HOME/go/binに出力されます(出力先は環境変数GOPATHGOBINで制御できます)。

# 実行可能なバイナリファイルを$HOME/go/binに出力
go install
# 出力した実行可能ファイルを実行
export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
myapp

gitを利用している場合は、ここでコミットをしましょう。

cd $HOME/myapp
git init
git add go.* *.go
git commit -m "first commit"

これでリポジトリとGoのモジュールを作成し、最初のプログラムを実行できました。

コードを整形する

続いて、コードの見た目を綺麗にする方法を説明します。

Goではコードの見た目を整形するためのツールが用意されています。 このツールのお陰で、誰が書いても同じような見た目のコードに統一できます。

先ほどのmyapp.goを次の内容に書き換えてください。 これは、意図的に見づらいコードを書いたものです。

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

次にgo fmtを用いてこの見づらいコードを整形します。

cd $HOME/myapp
go fmt

myapp.goが次のように整形されたことを確認します。

package main

import (
    "fmt"
)

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

エディタによってはファイル保存時に自動でgo fmtを実行する機能があります。 お使いのエディタの設定を確認しておくと良いでしょう。

これでコードの整形ができました。

他のパッケージを使う

続いて、他のパッケージを作成し、それを利用するコードを書いてみましょう。 今回は計算をするcalcパッケージを作成します。

先ほどのリポジトリのルートディレクトリにcalcディレクトリを作成し、その中にcalc.goを作成します。

cd $HOME/myapp
mkdir calc
touch calc/calc.go

calc.goは次の内容とします。

// パッケージ名はディレクトリ名と同じにするとわかりやすい
package calc

// Add関数は2つの整数を受け取って、それを足した結果を返す。
// 他のパッケージから利用するため、関数名の1文字目を大文字にする。
func Add(x int, y int) int {
    return x + y
}

Add関数は大文字で始まるため、他のパッケージから利用できます。

あわせて、myapp.goAdd関数を利用するように書き換えます。

package main

import (
    "fmt"

    // calcパッケージを利用する
    // インポートパスは『モジュールパス』と『モジュールのサブディレクトリ』を
    // 結合したもの
    "example.com/user/myapp/calc"
)

func main() {
    // calcパッケージのAdd関数を実行する
    // 1 + 2 の結果を表示する
    fmt.Println(calc.Add(1, 2))
}

go runコマンドを使ってプログラムを実行します。

go run .

次の内容が画面に表示されます。

3

これで他のパッケージを作成し、それを利用したコードを書くことができました。

リモートモジュールを利用する

続いて、インターネット上に公開されているモジュールを利用してみましょう。 今回はGoogleが公開している『データの比較をするモジュール』を使います。

myapp.goを次の内容に書き換えます。 import文にgithub.com/google/go-cmp/cmpを追加しました。先述の通り、モジュールパスは公開先URLです。 実際に公開先のモジュールhttps://github.com/google/go-cmpが存在することを確認してみましょう。

package main

import (
    "fmt"

    "example.com/user/myapp/calc"
    "github.com/google/go-cmp/cmp"
)

func main() {
    // calcパッケージのAdd関数を実行する
    // 1 + 2 の結果を表示する
    fmt.Println(calc.Add(1, 2))
    // 2つの値を比較して違いがあれば表示する
    fmt.Println(cmp.Diff(1, 2))
}

go mod tidyコマンドを使ってソースファイルのimport文を参照し、必要なモジュールをダウンロードします。

go mod tidy
cat go.mod

go.modgo.sumには使用するモジュールとしてgithub.com/google/go-cmpの情報が追加されます。

module example.com/user/myapp

go 1.15

require github.com/google/go-cmp v0.5.2

go runコマンドを使ってプログラムを実行します。

cd $HOME/myapp
go run .

画面には次の内容が表示されます。

3
  int(
-       1,
+       2,
  )

これでリモートモジュールを利用したコードを書くことができました。

テストする

続いて、コードをテストしましょう。

Goにはあらかじめテストをするための仕組みが備わっています。

ここではcalc.goのテストとしてcalc_test.goを作成します。 ファイル名の末尾が_test.goであるファイルは、テスト用のソースファイルとして扱われます。

cd $HOME/myapp
touch calc/calc_test.go

関数名がTestで始まり、関数のシグネチャはTestXxx(t *testing.T)である関数を作成します。 Goはこのような関数をテスト関数として実行します。 t.Errorメソッドを呼び出すと、テスト失敗とみなされます。

calc_test.goに次の内容を書きます。

package calc

import "testing"

// テスト用の関数は関数名をTestで始める
func TestAdd(t *testing.T) {
    cases := []struct {
        inX, inY, want int
    }{
        // 次の形式で、関数の入力値と期待値をいくつか書く
        // {xの値, yの値, xとyを足し算した値(期待する処理結果)}
        {1, 2, 3},
        {2, 3, 5},
        {-1, 1, 0},
    }
    for _, c := range cases {
        // Add関数を実行する
        got := Add(c.inX, c.inY)
        // Add関数の実行結果が期待値と一致することを確認する
        // 不一致の場合、テストに失敗したことを通知する
        if got != c.want {
            t.Errorf("Add(%d, %d) == %d, want %d", c.inX, c.inY, got, c.want)
        }
    }
}

go testコマンドを使ってテストを実行します。 引数にはテスト対象のパッケージパスを指定します。 ./...は現在のディレクトリとそのサブディレクトリすべてを指します。

cd $HOME/myapp
go test ./...

テストが成功すると、次の内容が画面表示されます。calcパッケージの行がokであることを確認します。

?       example.com/user/myapp  [no test files]
ok      example.com/user/myapp/calc     0.053s

お疲れ様でした。これでコードを書くための一連の作業を体験できました。