Go言語のpackageを使ってパッケージを定義し、管理する方法についてわかりやすく解説

スポンサーリンク
スポンサーリンク
amazon
スマイルSALE
--:--:--
ad. 価格範囲を指定して商品を探せます

packageの概要

プログラムのパッケージを宣言するGoの予約語

package

概要 packageは、Goのソースコードを機能や役割ごとにカプセル化し、独立した名前空間(モジュール単位)を構築するための最重要キーワードです。Go言語のすべてのソースファイルは、必ずいずれかのpackage宣言から書き始めなければなりません。

  • Goのソースコードは必ず1つのpackageに属する。
  • パッケージを使うことで、コードの再利用性と管理性が向上する。
  • エントリーポイントとなるmainパッケージを持つプログラムは実行可能ファイルとしてコンパイルされる。

packageの考え方をイメージで理解

基本的なpackageの使い方

Goのプログラムは必ずパッケージの宣言から始まります。

package main

import "fmt"

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

解説:

  • package mainを宣言することで、Goのエントリーポイントを示します。
  • このプログラムはmain関数を含むため、実行可能なプログラムとして動作します。

実行結果:

Hello, Go!

カスタムパッケージの作成と使用

Goでは独自のパッケージを作成し、それを別のファイルで使用できます。

1. カスタムパッケージ(mypackage/greet.go)

package mypackage

import "fmt"

// Greet はパッケージ内の関数
func Greet(name string) {
    fmt.Println("こんにちは,", name)
}

2. カスタムパッケージを使う(main.go)

package main

import (
    "myproject/mypackage" // カスタムパッケージをインポート
)

func main() {
    mypackage.Greet("太郎")
}

解説:

  • mypackageというパッケージを作成し、Greet関数を定義。
  • メインプログラム(main.go)でmypackageをインポートし、mypackage.Greetを呼び出す。自作パッケージをインポートする場合、プロジェクトのルートにある go.mod ファイル内で宣言した module モジュール名を起点とした、絶対パス表記(例: "モジュール名/フォルダ名")で記述する必要があります。
  • カスタムパッケージの関数を外部から利用するためには、関数名を大文字で始める必要があります。Go言語には public や private といったアクセス修飾子キーワードがありません。その代わりに、識別子の 最初の1文字が大文字(公開)か小文字(非公開)か という極めてシンプルなルールによって、外部パッケージに対する可視性が厳格に決定されます。

実行結果:

こんにちは, 太郎

標準ライブラリのパッケージを使用

Goには豊富な標準ライブラリがあり、パッケージをインポートして利用できます。

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("現在の時刻:", time.Now())
}

解説:

  • import "fmt" で標準出力用のfmtパッケージをインポート。
  • import "time" で時刻関連 of 処理を提供するtimeパッケージをインポート。

実行結果(実行時の日時による):

現在の時刻: 2026-06-18 21:24:39 +0900 JST

パッケージのエイリアス(別名)を使う

長いパッケージ名を短縮したり、同じ名前を持つ別々のパッケージを同時に読み込む際の競合を避けるためにエイリアス(別名)を設定できます。

package main

import (
    f "fmt" // fmtパッケージにfというエイリアスを付与
)

func main() {
    f.Println("エイリアスを使った出力")
}

解説:

  • import f "fmt" のように、パッケージに別名をつけられます。この設定を行うと、元の名前(fmt)では参照できなくなり、完全に新しいエイリアスへと名前空間が置き換わります。
  • f.Println() と記述することで、短縮して呼び出せます。

実行結果:

エイリアスを使った出力

init関数を使ったパッケージの初期化

Goのパッケージは、インポートされた際に init 関数を自動実行できます。

package mypackage

import "fmt"

func init() {
    fmt.Println("mypackageが初期化されました")
}

func Greet(name string) {
    fmt.Println("こんにちは,", name)
}

解説:

  • init()関数は、パッケージがインポートされた際に自動的に実行される特殊な関数です。引数も戻り値も持たず、該当パッケージが別コードからインポートされた際、main 関数が実行されるよりも前にランタイムによって自動的に1度だけ呼び出されます。
  • この仕組みを利用して、初期設定やデータの準備、あるいはグローバル変数の初期化やドライバの事前登録といったセットアップ処理を行うことができます。1つのパッケージ内に複数の init() 関数を定義することも可能です。

学術的・設計的注意点

  • パッケージ名とディレクトリ名の一致原則と混在禁止: Go言語の強力な慣習およびツールチェーン上の要請として、あるディレクトリ内に配置するGoファイルのパッケージ名は、そのディレクトリの名前と完全に一致させる 必要があります。また、1つのディレクトリの中に、異なるパッケージ名が 混在することは絶対に許されません。これを行うと、ビルド時にコンパイラから致命的なエラーが出力され、コンパイルが即座に中断されます。ただし、唯一の例外として、テスト用のファイル(xxx_test.go)に限り、ブラックボックステストを強制するために同じディレクトリ内で package foo_test という別パッケージを宣言することが公式に認められています。
  • 未使用のパッケージはコンパイルエラー: インポートしたパッケージを使わないと 警告ではなくコンパイルエラー になります。これは「不要なコードを一切残さず、コンパイル速度を最速に保ち、生成されるバイナリサイズを極限まで軽量化する」という強い設計思想によるものです。
  • 循環インポートは不可: AがBをインポートし、BがAをインポートするような構成は コンパイルエラー(import cycle not allowed) となり、厳格に遮断されます。パッケージ間の依存グラフが必ず一方通行(非巡回グラフ)になるように設計をコントロールする必要があります。

よくある質問

Q: すべてのGoプログラムはパッケージを定義する必要がありますか?
A: はい。Goのすべてのソースコードは例外なく package 宣言から始まる必要があります。
Q: Goのプログラムを実行するには、どのパッケージが必要ですか?
A: 独立して実行可能なプログラム(アプリケーション)をビルドする場合は、必ず package main を持ち、その内部に main() 関数を含める必要があります。
Q: パッケージ内の関数や変数を外部からアクセスする方法は?
A: 名前(識別子)の 最初の1文字を大文字で始める ことで、外部パッケージからアクセス可能な「エクスポート」状態になります。小文字で始まるものは非公開となり、同一パッケージ(同じディレクトリ内のファイル群)の内部からしか見えません。
Q: import時にパッケージを使わないとどうなりますか?
A: 前述の通りコンパイルエラーになります。ただし、パッケージ名の前にアンダースコア(_)を配置する 空インポート を行うと、そのパッケージの init() 関数による初期化ロジックだけをシステムに組み込みつつ、未使用エラーを回避できます(例:import _ "github.com/lib/pq")。
Q: internal という名前のディレクトリ(パッケージ)があると聞きましたが、これは何ですか?
A: ディレクトリ名を internal にすると、その internal ディレクトリの親ディレクトリを起点としたツリーの外部からは、大文字でエクスポートされたメンバーであってもインポートして呼び出すことが不可能 になる強力なアクセス制御機能です。自作モジュールの内部だけで共有したい非公開ライブラリを作り、外部のサードパーティ開発者に勝手に依存されたくない(内部実装を隠蔽したい)場合に重用される高度な設計構造です。

まとめ

  • packageは、名前空間を区切り、コードの再利用性とカプセル化(モジュール化)を制御するための最優先宣言キーワード。
  • エントリーポイントは package mainmain() 関数の組み合わせで定義される。
  • 外部への公開有無は 識別子の先頭1文字が大文字(公開)か小文字(非公開)か のみによってコンパイル時に厳格に判別される。
  • 同一ディレクトリ内のファイルはすべて同一のパッケージ名を持つ必要があり、内部の小文字メンバーを相互に自由に参照できる性質を持つ。
  • パッケージの初期化・事前セットアップには init() 関数を活用する。