Go言語のモジュールとパッケージ
2026/07/28

モジュールとgo.mod

モジュールは、ルートにgo.modを置いたソースの木です。依存の境界であり、go.modmodule行がモジュールパスになります。そのモジュール内のパッケージのimportパスは、このモジュールパスを先頭にします。

mkdir demo
cd demo
go mod init example.com/demo
module example.com/demo

go 1.26

go行は、このモジュールが想定する言語の版です。サブディレクトリごとにgo mod initする必要はありません。同じgo.modの下にあるパッケージは、すべてこのモジュールに属します。

外部パッケージをimportすると、必要なモジュール版がgo.modに記録されます。依存の整理にはgo mod tidyが使われます。

パッケージ

パッケージはコンパイルの単位であり、識別子の名前空間でもあります。通常、1つのディレクトリが1つのパッケージです。同じディレクトリの.goファイルは、同じpackage句を書き、まとめてコンパイルされます。あるパッケージの識別子を、別パッケージから自由に全部使えるわけではありません。名前の先頭が大文字か小文字かで、外から見えるかどうかが決まります。

// greeter/greet.go
package greeter

func Hello(name string) string {
    return "hello, " + name
}
// main.go
package main

import (
    "fmt"

    "example.com/demo/greeter"
)

func main() {
    fmt.Println(greeter.Hello("Go"))
}

greeterディレクトリのimportパスは、モジュールパスに相対ディレクトリを足したexample.com/demo/greeterです。

importパスとパッケージ名

importパスは、import "..."に書く文字列です。モジュールパスにディレクトリを足したもので、「どのフォルダのコードを取り込むか」を指定します。

パッケージ名は、取り込んだあとにコード上で使う名前です。そのディレクトリの.goファイル先頭のpackage句で決まります。importパスの末尾と同じとは限りません。

揃っている例です。ディレクトリgreeter/のソースがpackage greeterなら、呼び出しもgreeterで書きます。

// greeter/greet.go
package greeter

func Hello(name string) string {
    return "hello, " + name
}
import "example.com/demo/greeter" // importパス(場所)

greeter.Hello("Go") // パッケージ名(package句)

package句だけ違うと、importはできても呼び出し名が変わります。たとえばpackage greetならgreet.Helloであり、greeter.Helloは見つかりません。

// greeter/greet.goがpackage greetのとき
import "example.com/demo/greeter"

greet.Hello("Go") // 使えるのはpackage句の名前
// greeter.Hello("Go") // コンパイルエラー

パッケージ名は短い小文字の単語1つが望ましいです(httpjsonbufioなど)。呼び出し側は常にパッケージ名.識別子と書くため、型名や関数名にパッケージ名を重ねると冗長になります。たとえばバッファ付きの読み取り型はbufio.Readerであり、bufio.BufReaderのようにはしません。

公開と非公開

識別子の公開範囲は、名前の先頭文字で決まります。大文字で始まる名前は他パッケージから参照でき、小文字で始まる名前は同じパッケージ内だけです。

package greeter

func Hello() string { // 他パッケージから呼べる
    return hello()
}

func hello() string { // 同じパッケージ内だけ
    return "hello"
}
import "example.com/demo/greeter"

greeter.Hello()
// greeter.hello() // コンパイルエラー: 小文字は他パッケージから見えない

import宣言

ソースはpackage句のあと、import、その後に宣言が続きます。

import "fmt"

import (
    "errors"
    myio "io"
    _ "image/png"
)

別名を付けると、パッケージ名の衝突を避けられます。myio "io"ならmyio.Readerのように使います。

識別子を使わず、パッケージの初期化だけ実行したいときは_ "image/png"のようにブランクimportします。デコーダの登録など、initの副作用が目的のときに使います。

import . "fmt"は、パッケージ名なしで識別子を取り込む書き方です。通常ならfmt.Printlnと書くところを、Printlnだけで呼べます。

import . "fmt"

func main() {
    Println("hello") // fmt.Println("hello")と同じ
}

Printlnが自パッケージの関数なのかfmtのものなのか、呼び出し箇所だけでは分かりにくくなります。そのため通常のコードでは使いません。

internalパッケージ

パスにinternalを含むパッケージは、そのinternalの親ディレクトリ以下からだけimportできます。モジュール全体から見えるわけではなく、ディレクトリの位置で決まります。

たとえばexample.com/demo/internal/storeは、example.com/demo/...配下からはimportできますが、別モジュールやexample.com/demoの外にあたるパスからはimportできません。公開したくない実装を内側に閉じるための仕組みです。

ドキュメントコメント

公開する宣言の直前に書いたコメントが、ドキュメントとして扱われます。

// ParseConfigは設定ファイルを読み込む。
func ParseConfig(path string) (*Config, error) {
    // ...
}

フォーマット

Goのソースは、手で細かく揃えるのではなくgofmt(パッケージ単位ならgo fmt)で機械的に整形するのが慣習です。インデントはタブ、空白の入れ方や並びも標準形に揃います。スタイルの好みで議論せず、ツールの出力に合わせます。

ファイルをその場で書き換える例です。

gofmt -w main.go

パッケージ(ディレクトリ)単位で揃えるときはgo fmtを使います。結果のスタイルはgofmtと同じです。

go fmt ./...

gofmtは標準入力の断片も整形できます。go fmtはパスで指定したパッケージ配下の.goファイルをまとめて扱います。

テスト

テストは、対象と同じディレクトリに_test.goで終わるファイルを置きます。書き方は2通りあり、小文字の識別子を直接試すかどうかで選びます。

同じpackage greeterで書くと、小文字の識別子も含めパッケージ内部が見えます。非公開の補助関数や内部状態まで検証したいときに使います。importは不要で、同じパッケージの関数をそのまま呼べます。

// greeter/greet_test.go
package greeter

import "testing"

func TestHello(t *testing.T) {
    if Hello("Go") != "hello, Go" {
        t.Fatal("unexpected greeting")
    }
}

package greeter_testで書くと、他パッケージからの利用と同じです。importが必要で、大文字の公開APIだけを試せます。利用側と同じ見え方で公開面を確認したいときに使います。非公開の識別子はここからは触れません。

// greeter/greet_ext_test.go
package greeter_test

import (
    "testing"

    "example.com/demo/greeter"
)

func TestHello(t *testing.T) {
    if greeter.Hello("Go") != "hello, Go" {
        t.Fatal("unexpected greeting")
    }
    // greeter.hello() // コンパイルエラー: 他パッケージからは見えない
}

どちらもよく使われます。非公開まで含めて試すなら同じパッケージ、公開APIだけを外から試すならgreeter_test、という分け方になります。1つのディレクトリに両方を置くこともできます。