package キーワードは、Go のソースファイルが属するパッケージの名前を指定します。すべての .go ファイルで、最初のコード行に書く必要があります。package main は実行可能なプログラムとしてビルドされ、package billing のようにそれ以外の名前を付けると、ほかのパッケージから import されるライブラリとしてビルドされます。1 つのディレクトリにあるファイルはすべて同じパッケージ名を使う必要があります。billing.Total のように、ほかのコードはその名前の後にドットを付けて、エクスポートされた名前にアクセスします(Go spec)。
要約
.goファイルはすべてpackage nameから始まります。その前に置けるのはコメントと空行だけです。package mainがプログラム本体です。引数も戻り値もないfunc main()が必要で、go buildでバイナリになります。import することはできません。- 1 つのディレクトリが 1 つのパッケージです。同じフォルダに異なるパッケージ名のファイルがあると、
found packages billing (invoice.go) and payments (tax.go)で失敗します。主な例外は、テストファイルで使う_testパッケージです。 - パッケージ名と import パスは別物です。
"gopkg.in/yaml.v3"を import して、yaml.Unmarshalを呼び出します。慣習では、名前をパスの最後の要素と一致させます。 - 良いパッケージ名は、短い小文字の 1 単語です。アンダースコアも mixedCaps も使わず、
utilやcommonも避けます。 - 可視性はパッケージ単位で決まります。大文字で始まる名前は、ほかのパッケージにエクスポートされます。小文字で始まる名前は、同じパッケージ内のすべてのファイルから見えますが、それ以外からは見えません。
Go の package main の役割とは?
package main は、ライブラリではなく実行ファイルをビルドするよう Go のツールチェーンに指示します。そのパッケージの main 関数がプログラムの開始点で、この関数が戻るとプログラムは終了します。次のコードは、1 ファイルで完結する HTTP サービスです。
example.gogopackage main import ( "fmt" "log" "net/http" ) func main() { mux := http.NewServeMux() mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "ok") }) log.Fatal(http.ListenAndServe(":8080", mux)) }
go run . でコンパイルして実行でき、go build -o bin/api . でバイナリを書き出せます。main に関する 2 つのルールは、どちらもツールチェーンが強制します。main 関数のない package main は、リンクの段階で失敗します。
example.texttextruntime.main_main·f: function main is undeclared in the main package
また、go run ./billing でライブラリパッケージを実行しようとすると、コンパイルが始まる前に失敗します。
example.texttextpackage example.com/shop/billing is not a main package
main はプログラムそのものなので、ほかのパッケージから import することはできません。試すと import "example.com/shop/cmd/api" is a program, not an importable package というエラーになります。そのため Go のプロジェクトでは、ロジックをライブラリパッケージに置き、main を小さく保ちます。複数のバイナリを持つリポジトリでは、それぞれを専用のディレクトリ(通常は cmd/ の下)に置き、各ディレクトリが独立した package main になります。LevelUpGo 自身のバックエンドには、API サーバーからコンテンツインポーターまで 11 個の package main があり、すべてが internal/ 以下の 22 個のライブラリパッケージを共有しています。
同じディレクトリのファイルで異なるパッケージ名を使えるか?
使えません。go コマンドはディレクトリを 1 つのパッケージとして扱うので、その中の .go ファイルはすべて同じ名前を宣言する必要があります。たとえば billing/invoice.go が package billing で始まっていて、誰かが package payments と書いた billing/tax.go を追加したとします。go build ./billing は次のエラーで止まります。
example.texttextfound packages billing (invoice.go) and payments (tax.go) in /home/dev/shop/billing
修正するには、どちらかの package 句の名前を変えるか、ファイルを専用のディレクトリに移します。ディレクトリは import できる最小の単位でもあります。2 つのファイルが別々のパッケージに属するなら、フォルダも分ける必要があります。
一方、1 つのパッケージを複数のファイルに分けても、何のコストもかかりません。ディレクトリ内の各ファイルからは、エクスポートされているかどうかにかかわらず、ほかのファイルの名前が import なしで見えます。invoice.go、tax.go、refund.go からなる billing パッケージは、1 つの長いファイルとまったく同じように振る舞います。
パッケージ名と import パスの違いとは?
import パスはパッケージの場所を表し、パッケージ名はコードの中で使う識別子です。import パスは go.mod のモジュールパスにディレクトリを足したもので、example.com/shop/billing のようになります。パッケージ名は、そのディレクトリ内のファイルにある package 句で決まります。
慣習では両者をそろえるので、名前はパスの最後の要素になります。次の表は、両者が一致するパスを 1 つと、よくある 2 つの例外を示しています。例外はどちらもバージョンが原因です。
| import パス | パッケージ名 | 異なる理由 |
|---|---|---|
net/http | http | 一致している |
gopkg.in/yaml.v3 | yaml | パスに .v3 というバージョンの接尾辞が付いている |
math/rand/v2 | rand | メジャーバージョンのディレクトリ /v2 |
github.com/jackc/pgx/v5 | pgx | メジャーバージョンのディレクトリ /v5 |
2 つの import が同じ名前を持つ場合は、import ブロックで一方の名前を変えます。セッショントークンを生成し、リトライのジッターも加えるコードでは、crypto/rand と math/rand/v2 の両方が必要になることがよくあります。どちらも名前は rand です。
example.gogopackage tokens import ( "crypto/rand" "encoding/hex" mrand "math/rand/v2" "time" ) // NewSessionID returns a random 32-character hex session ID. func NewSessionID() string { b := make([]byte, 16) rand.Read(b) return hex.EncodeToString(b) } // RetryDelay adds up to 100ms of jitter to a base delay. func RetryDelay(base time.Duration) time.Duration { return base + time.Duration(mrand.IntN(100))*time.Millisecond }
この名前の変更は、そのファイルの中だけで有効です。ほかのファイルからは、パッケージが宣言した名前のまま見えます。エイリアスやブランク import など、ほかの import の書き方は Go の import キーワードで解説しています。
フォルダ名と異なる名前のパッケージをディレクトリに置いても、Go はコンパイルします。そのしわ寄せは読む人に来ます。example.com/shop/httpapi-v2 を import した人は、パッケージ名が httpapi だと知るためにソースを開かなければなりません。バージョンの接尾辞のために違いが避けられない場合を除き、両者はそろえておきましょう。
Go のパッケージ名はどう付けるべきか?
Go のパッケージ名には、Effective Go と Go ブログの記事 Package names に基づく、いくつかの短いルールがあります。
- 小文字の 1 単語にする。
billing、httpapi、ratelimitのようにします。rate_limitやrateLimitは使いません。パッケージ名は任意の識別子でよいので言語仕様上は許されていますが、Go らしいコードでは使いません。 - 短く具体的にする。名前はすべての呼び出し箇所で入力します。
stringconversionよりstrconv、authenticationserviceよりauthのほうが適しています。 - 提供するものにちなんで名付ける。
util、common、helpers、misc、modelsは、中に何があるかを何も伝えません。そして、あらゆるパッケージから import される大きなパッケージに育ってしまいます。代わりにmoney、slugify、paginationのように目的ごとに分割します。 - エクスポートする名前でパッケージ名を繰り返さない。呼び出し側は必ずパッケージ名を書くからです。
http.Serverは正しく、http.HTTPServerは誤りです。billing.Totalは正しく、billing.BillingTotalは誤りです。パッケージの主要な型のコンストラクタは、ring.Newのように単にNewとすることがよくあります。 - キーワードや
_を使わない。package typeは構文エラーになり、package _はinvalid package name _で失敗します。
名前は、エクスポートされた識別子と組み合わせて読んでみてください。呼び出し側からは必ずその形で見えるからです。ratelimit.New(100, time.Minute) や config.Load("app.yaml") は文章のように読めますが、utils.NewRateLimiterUtil はそうではありません。
Go の package は可視性をどう制御するのか?
Go には public、private、protected といったキーワードがありません。その代わりに、パッケージが境界になります。大文字で始まる名前はエクスポートされ、それを import するどのパッケージからも見えます。小文字で始まる名前はエクスポートされず、自身のパッケージの中でだけ、そのすべてのファイルから見えます。
example.gogo// Package billing calculates invoice totals and applies VAT. package billing // VATPercent is the standard VAT rate applied to invoices. const VATPercent = 25 // Total returns the sum of line items in cents, including VAT. func Total(lineItems []int64) int64 { var sum int64 for _, c := range lineItems { sum += c } return addVAT(sum) } func addVAT(cents int64) int64 { return cents + cents*VATPercent/100 }
package main のハンドラーは billing.Total を呼び出せますが、billing.addVAT(1000) は name addVAT not exported by package billing で失敗します。同じルールは struct のフィールドとメソッドにも当てはまります。encoding/json が struct のエクスポートされたフィールドしか扱えないのも、同じ理由です。
自分の複数のパッケージで共有したいものの、外部のモジュールからは import させたくないコードのために、Go には internal ディレクトリがあります。example.com/shop/internal/pricing にあるパッケージは、example.com/shop 以下にあるものからは import できますが、それ以外からはできません。別のモジュールが import しようとすると、次のエラーになります。
example.texttextuse of internal package example.com/shop/internal/pricing not allowed
internal/ を使うべき場面や、実際のサービスで cmd/ とライブラリパッケージをどう配置するかは、Go のプロジェクト構成で解説しています。
Go の _test パッケージとは?
_test.go で終わるテストファイルでは、2 つのパッケージ名のどちらかを使えます。package billing にすると、テストはパッケージの内部に置かれるので、エクスポートされていない関数も呼び出せます。
example.gogopackage billing import "testing" func TestAddVAT(t *testing.T) { if got := addVAT(1000); got != 1250 { t.Fatalf("addVAT(1000) = %d, want 1250", got) } }
package billing_test にすると、外部テストパッケージになります。同じディレクトリに置かれますが、ほかの呼び出し側と同じように billing を import する必要があり、使えるのはエクスポートされた名前だけです。
example.gogopackage billing_test import ( "testing" "example.com/shop/billing" ) func TestTotal(t *testing.T) { got := billing.Total([]int64{1000, 2000}) if got != 3750 { t.Fatalf("Total = %d, want 3750", got) } }
//go:build ignore を付けたジェネレータスクリプトのように、ビルド制約で除外されるファイルを別にすれば、1 つのディレクトリに 2 つのパッケージ名を共存させる方法は _test という接尾辞だけです。go test は 2 つのパッケージを別々にビルドします。外部テストは、呼び出し側と同じ使い方で API を検証します。標準ライブラリが、ドキュメントに表示される実行可能な Example 関数を書くのにも外部テストを使っています。多くの Go のコードベースでは両方を併用し、複雑な内部処理にはパッケージ内のテストを、公開された振る舞いには _test パッケージを使っています。
package 句の前には何を書けるのか?
書けるのはコメントと空行だけです。その位置に置くコメントのうち 2 種類は、ツールチェーンにとって意味を持ちます。
パッケージのドキュメントコメントは、空行を挟まずに package の直前に置きます。慣習では Package billing で始め、go doc や pkg.go.dev はこれをパッケージのドキュメントとして表示します。大きなパッケージでは、このコメントだけを書いた doc.go というファイルに置くことがよくあります。package main のコマンドでは、// Api serves the shop's HTTP API. のように、代わりにプログラムの説明を書きます。
ビルド制約は、そのファイルをコンパイルするかどうかを決めます。package 句より前に書き、その後に空行を入れる必要があります。
example.gogo//go:build debug package billing import "log" func init() { log.Println("billing: debug build") }
このファイルは go build -tags debug のときだけコンパイルされます。watch_linux.go や asm_arm64.go のように OS やアーキテクチャの接尾辞が付いたファイルは、コメントがなくてもファイル名によって同じ扱いを受けます。
この例では init も使っています。パッケージは init 関数をいくつでも宣言でき、1 つのファイルに複数書くこともできます。Go はパッケージレベルの変数を初期化した後、そのパッケージを import しているものが動き出す前に init を実行します。import されたパッケージは、それを import するパッケージより先に完全に初期化されるので、main は常に最後に実行されます。init はドライバーの登録のような軽い初期設定だけに使いましょう。失敗する可能性のある処理は、エラーを返す関数に書くべきです。
LevelUpGo で学ぶ
LevelUpGo では、ブラウザ上で本物の Go コードを実行する演習を通じて Go を学べます。Packages & Organization コースでは、エクスポートされる名前、init とパッケージの状態、Go モジュール、プロジェクト構成を扱います。Code Organization では、大きくなっていくコードベースを、わかりやすい名前のパッケージに分割する方法を紹介します。Go を始めたばかりなら、まず Go Basics から取り組んでください。残り 24 個の予約語については、Go のキーワード一覧:全 25 個の予約語を解説をご覧ください。
よくある質問
Go のパッケージ名はディレクトリ名と違っていてもよいですか?
はい。コンパイラが読むのは package 句だけなので、httpapi-v2 というフォルダの中に package httpapi を置いても問題なくビルドできます。慣習では名前を import パスの最後の要素と一致させます。主な例外は、gopkg.in/yaml.v3(パッケージ yaml)や math/rand/v2(パッケージ rand)のようなバージョンの接尾辞です。
Go のプログラムにはなぜ package main が必要なのですか?
go コマンドが実行ファイルをビルドするのは main という名前のパッケージからだけで、ランタイムはそのパッケージの main 関数を呼び出してプログラムを開始します。それ以外のパッケージはすべてライブラリです。func main() のない package main は function main is undeclared in the main package で失敗します。
1 つの Go モジュールに package main を複数置けますか?
はい、ディレクトリごとに 1 つずつ置けます。API サーバーとマイグレーションツールを持つサービスには、通常 cmd/api/main.go と cmd/migrate/main.go があり、どちらも package main を宣言しています。それぞれ、go run ./cmd/api や go build ./cmd/migrate のようにディレクトリを指定してビルドまたは実行します。
Go のパッケージ名にアンダースコアや大文字を使えますか?
コンパイラはどちらも許可します。パッケージ名は _ 以外の有効な識別子なら何でもよいからです。しかし Go のスタイルでは、使わないことになっています。Go ブログの記事 Package names は、strconv や httputil のように、短く小文字で「under_scores や mixedCaps を含まない」名前を求めています。
Go で package はキーワードですか?
はい。package は Go の 25 個の予約語の 1 つなので、変数名や関数名には使えません。パッケージを表す変数が必要なとき、Go のコードでは通常 pkg という名前を付けます。
出典
- The Go Programming Language Specification, Package clause: https://go.dev/ref/spec#Package_clause
- The Go Programming Language Specification, Program initialization and execution: https://go.dev/ref/spec#Program_initialization_and_execution
- The Go Programming Language Specification, Exported identifiers: https://go.dev/ref/spec#Exported_identifiers
- Effective Go, Package names: https://go.dev/doc/effective_go#package-names
- The Go Blog, Package names: https://go.dev/blog/package-names
- Go Doc Comments, Packages and Commands: https://go.dev/doc/comment#package
- Organizing a Go module: https://go.dev/doc/modules/layout
- Command go, Internal Directories: https://pkg.go.dev/cmd/go#hdr-Internal_Directories
- Command go, Build constraints: https://pkg.go.dev/cmd/go#hdr-Build_constraints
- Command go, Test packages: https://pkg.go.dev/cmd/go#hdr-Test_packages
