パッケージ

パッケージは、同じディレクトリにあるソースファイルの集合です。

パッケージの種類

パッケージには次の種類があります。

  • 標準パッケージ:Goが最初から用意しているもの(一覧は公式ドキュメントに公開)
  • サードパーティパッケージ:第三者が用意したもの
  • 自作パッケージ:自分で用意したもの

これまでは標準パッケージを利用してきたものの、自分のコードは1ソースファイル1パッケージによる構成でした。 ここでは自作パッケージを作成・利用するために、ソースファイル分割とパッケージ分割の方法を説明します。

ソースファイルの分割

パッケージを分ける前に、同じパッケージの中でソースファイルを分ける方法を説明します。

ソースファイルは型・定数・変数・関数で構成されています。 これらは同じパッケージに所属する他のソースファイルから参照できます。

// このファイルが所属するパッケージ
package main

// このファイルが利用するパッケージ
import (
    "fmt"
)

/* ここから下は同じパッケージの他のソースファイルから参照できる */

// 型
type myint int

// 定数
const (
    c1 = 10
    c2 = 20
)

// 変数
var (
    v1 = 1
    v2 = 2
)

// 関数
func main() {
    fmt.Println(v1, v2)
    fmt.Println(c1, c2)

    m := myint(1)
    fmt.Println(m)
}

次のサンプルは定数aを別ソースファイルに分割したものです。 main.gomain2.goは同じmainパッケージに所属しているため、main.goからmain2.goの定数aを参照できます。

main.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println(a)
}

main2.go

package main

const a = 1

同じパッケージの中で同じ識別子を使うことはできません。 先ほどのサンプルに次のソースファイルを加えると、識別子aの重複によりコンパイルエラーとなります。

main3.go

package main

var a = 1

パッケージの初期化(init関数)

パッケージを初期化するための特別な関数として、init関数があります。

init関数には次の特徴があります。

  • mainパッケージのmain関数より先に実行される
  • Goは同じ識別子を複数回宣言できないが、init関数は例外で複数宣言できる

次のサンプルはmainパッケージの2箇所にinit関数を定義したものです。

main.go

package main

import (
    "fmt"
)

func init() {
    fmt.Println("main.go init")
}

func main() {
    fmt.Println(a)
}

main2.go

package main

import (
    "fmt"
)

const a = 1

func init() {
    fmt.Println("main2.go init")
}

パッケージの分割

他のパッケージを作成し、それを利用する方法は次の通りです。

  • 新たにディレクトリを作成する
  • 作成したディレクトリにソースファイルを作成する
  • 識別子の最初の文字を大文字にする(他のパッケージからアクセス可能になる)

次のサンプルはmainパッケージから別に作成したanotherパッケージを利用します。サンプルの中にはモジュールやgo.modなどこれまで説明していないものが出てきますが、これらは他のページで説明します。

サンプルのファイル構成は次の通りです。ひとつのディレクトリにひとつのパッケージが対応します。理由がなければディレクトリ名とパッケージ名はあわせておくとわかりやすいです。

ルートディレクトリ (パッケージ:main)
├── go.mod (モジュールを管理するファイル)
├── example.go (mainパッケージのソースファイル)
└── anotherディレクトリ (パッケージ:another)
     └── another.go (anotherパッケージのソースファイル)

go.mod

module example

example.go

package main

import (
    // anotherパッケージの利用を宣言する
    // インポートパスは次の2つをスラッシュで結合したもの
    // ・go.modに記載のモジュールパス(example)
    // ・モジュール内のサブディレクトリ(another)
    "example/another"
    "fmt"
)

func main() {
    // anotherパッケージの定数にアクセスする
    // 形式: パッケージ名.識別子
    fmt.Println(another.Num)
}

another/another.go

// 新たに作成したパッケージ
package another

// 他のパッケージからアクセスするため
// 識別子の最初の文字は大文字にする
const Num = 1

これでmainパッケージからanotherパッケージを利用できました。

別名インポート

インポートしたパッケージに別名をつけることができます。

package main

import (
    // パッケージの別名を指定
    f "fmt"
)

func main() {
    // 別名でアクセス
    f.Println("test")
}

ブランクインポート

パッケージしたインポートを参照しないことがあります。 その場合は、インポートパスの前に_を指定します。

package main

import (
    _ "fmt"
)

func main() {
    // インポートしたパッケージを参照しない
    return
}

ピリオドインポート

インポートパスの前に.をつけると、パッケージ名なしでアクセスできます。 ただし、インポート元とインポート先で同名の識別子があると、インポートできないので注意してください。

package main

import (
    . "fmt"
)

// インポート先と同名の識別子はNG
// const Println = 1

func main() {
    // パッケージ名は省略
    Println("test")
}