Goにおけるパッケージのインポート方法いろいろ～ローカル環境のパッケージをインポートしたい～


Goでパッケージのインポート方法がいろいろあって混乱したので整理してみました。
下の図に対応している章を見れば、インポート方法が分かるようになっています。

![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/537a3924-4ac5-de1d-1937-608a2d9fcc0b.png)


**目次**
- [1. 標準pkgをインポート](#1-標準pkgをインポート)
- [2. 自モジュール内のpkgをインポート](#2-自モジュール内のpkgをインポート)
- [3. ネット上から取得したpkgをインポート](#3-ネット上から取得したpkgをインポート)
- [4. GOPATH配下のpkgをインポート](#4-gopath配下のpkgをインポート)
- [5. 任意のローカルフォルダ内のpkgをインポート](#5-任意のローカルフォルダ内のpkgをインポート)

:::note warn
基本的には`モジュールモード`を想定していますが、[4. GOPATH配下のpkgをインポート](#4-gopath配下のpkgをインポート)の章のみ`GOPATHモード`を使用します。環境変数で`GO111MODULE=off`を設定してください。
:::

# 1. 標準pkgをインポート

まずは標準パッケージをインポートします。
ディレクトリ構造とファイルの中身は以下の通りです。
- モジュールのフォルダ直下に`go.mod`ファイルを配置しています。
- `main.go`は`main`パッケージという特別なパッケージとなっており、モジュールの直下に配置することが出来ます。
- 標準pkgのインポートは`go.mod`ファイル内に何かを記述する必要はありません。

:::note info
標準パッケージは`import "<pkg名>"`を記載するだけで読み込まれる。
:::


![1_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/5942dcda-de9f-104f-e29f-c758bb6d0fc5.png)

```go:go.mod
module mymodule

go 1.18
```

```go:main.go
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello World!")
}
```

<details><summary>実行例</summary>

```:実行例
D:\mymodule>go run main.go
Hello World!
```
</details>

```:実行例
D:\mymodule>go run main.go
Hello World!
```

# 2. 自モジュール内のpkgをインポート

ここでは自分自身のモジュール配下にあるパッケージのインポート方法について説明します。
ディレクトリ構造と各ファイルの中身は以下のようにしています。
- モジュールのフォルダ直下に`go.mod`ファイルを配置します。
- `main.go`は`main`パッケージという特別なパッケージとなっており、モジュールの直下に配置することが出来ます。
- パッケージはモジュールのサブフォルダとして作成します。なおパッケージ下に作成するファイル名は`mypkg.go`でなく`hoge.go`などでも問題なく読みこまれます。
- 自モジュール内のパッケージをインポートする際は`go.mod`に何かを記述する必要はありません。

:::note info
自モジュール内のパッケージは`import "<モジュール名>/<pkg名>"`と記載するだけで読み込まれる。
※ パッケージが複数階層になっている場合も`import "<モジュール名>/<フォルダ名>/<pkg名>"`のように記載すればよい。
:::


![2_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/58d4cf6a-b298-c533-e441-6c4bc7820597.png)


```go:go.mod
module mymodule

go 1.18
```

```go:main.go
package main

import (
	"fmt"
    // モジュールからのパスを記述
	"mymodule/mypkg"
)

func main() {
	fmt.Println("Hello World!")
	mypkg.HelloFromMypkg()
}
```

```go:mypkg.go
package mypkg

import (
	"fmt"
)

func HelloFromMypkg() {
	fmt.Println("Hello from mypkg!")
}
```

<details><summary>実行例</summary>

```:実行例
D:\mymodule>go run main.go
Hello World!
Hello from mypkg!
```
</details>



# 3. ネット上から取得したpkgをインポート

ここではインターネット上のレポジトリから取得したパッケージのインポート方法を説明します。
ディレクトリ構造とファイルの中身は以下の通りです。
- モジュールのフォルダ直下に`go.mod`ファイルを配置しています。
- `main.go`は`main`パッケージという特別なパッケージとなっており、モジュールの直下に配置することが出来ます。
- import文にはパッケージのパスを記述します。。
- インターネット上から取得したパッケージは`go.mod`ファイル内に`require`を記述する必要がありますが、自分自身で記述する必要はありません。`go mod tidy`コマンドか`go get <pkg名>`コマンドを実行すると自動で追記されます。

:::note info
インターネットから取得するパッケージは`import "<pkg名>"`を記載し、`go.mod`ファイルに`require`を記述すればよい。`go.mod`は手動で書くのではなく基本的には`go mod tidy`コマンドか`go get <pkg名>`コマンドを使う。
:::

![1_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/5942dcda-de9f-104f-e29f-c758bb6d0fc5.png)

```go:go.mod
module mymodule

go 1.18

require github.com/go-ping/ping v1.1.0

require (
	github.com/google/uuid v1.2.0 // indirect
	golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
	golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 // indirect
)
```

```go:main.go
package main

import (
	"fmt"
    // レポジトリ名を直接記述
	"github.com/go-ping/ping"
)

func main() {
	pinger, err := ping.NewPinger("www.google.com")
	if err != nil {
		panic(err)
	}
	pinger.SetPrivileged(true)
	pinger.Count = 2
	err = pinger.Run()
	if err != nil {
		panic(err)
	}
	fmt.Println(pinger.Statistics())
}
```

<details><summary>実行例</summary>

```:実行例
D:\mymodule>go run main.go
Hello World!
```
</details>

上記の例では、`go mod tidy`または`go get github.com/go-ping/ping`を実行すると、go.modファイルにrequireが自動で追記されます。

# 4. GOPATH配下のpkgをインポート

:::note warn
GOPATHを利用する際は`モジュールモード`ではなく`GOPATHモード`を使用します。環境変数で`GO111MODULE=off`を設定してください。
:::

ディレクトリ構成は以下のようにしています。
上記の注意書きにあるように、モジュールモードではなくGOPATHモードを利用するため、モジュールの概念は一旦忘れます（go.modなどはない）。

- インポート先のgoスクリプト（ここでは`main.go`）はmypkgフォルダ下に置いているが、作業フォルダはどこでも構いません。
- インポート対象のパッケージは`${GOPATH}/src`フォルダ下に配置します。
- モジュールモードではないため`go.mod`は不要です。

:::note info
GOPATHモードを用いて、${GOPATH}配下のパッケージを読み込む際は対象のパッケージを`${GOPATH}/src`下に配置し、`import "<pkg名>"`を記載するだけでよい。
:::

![4_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/89eeb30b-2c33-72b1-d4ac-0e13f7788b69.png)


```go:main.go
package main

import (
	"fmt"
	// パッケージ名を直接記述
	"yourpkg"
)

func main() {
	fmt.Println("Hello World!")
	yourpkg.HelloFromYourpkg()
}
```

```go:yourpkg.go
package yourpkg

import (
	"fmt"
)

func HelloFromYourpkg() {
	fmt.Println("Hello from yourpkg!")
}
```

<details><summary>実行例</summary>

```:実行例
D:\mypkg>go run main.go
Hello World!
Hello from yourpkg!
```
</details>


# 5. 任意のローカルフォルダ内のpkgをインポート

ここではローカル環境上の任意のフォルダ下にあるパッケージのインポート方法について説明します。
ディレクトリ構造と各ファイルの中身は以下のようにしています。
- モジュールのフォルダ直下には`go.mod`ファイルを配置します。**※インポート対象であるyourpkgの親フォルダにも`go.mod`は必要です。`go mod init`で作成するか、空のファイルを配置しておきます`**
- `main.go`は`main`パッケージという特別なパッケージとなっており、モジュールの直下に配置することが出来ます。
- パッケージはモジュールのサブフォルダとして作成します。なおパッケージ下に配置するファイル名は`mypkg.go`でなく`hoge.go`などでも問題なく読みこまれます。
- 以下の例ではmymoduleとyourmoduleを同じ階層においていますが、インポート対象のyourmoduleの配置先は任意です。
- `go.mod`内の`require`には`<モジュール名>/<pkg名>`の形でパッケージへのパスを記述します。
- `go.mod`内の`replace`によりインポート対象のパッケージをローカルのディレクトリから取得させるようにします。

:::note info
ローカルの任意のフォルダ上のパッケージは`import "<モジュール名>/<pkg名>"`でインポートし、`go.mod`には`require`と`replace`を記載する。
:::

![5_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/72f76dca-fbcb-79cf-9d13-1e4397dd45e1.png)


```go:mymodule/go.mod
module mymodule

go 1.18

require yourmodule v0.0.0

replace yourmodule => ../yourmodule
```

```go:main.go
package main

import (
	"fmt"
	//モジュールからのパスでインポート
	"yourmodule/yourpkg"
)

func main() {
	fmt.Println("Hello World!")
	yourpkg.HelloFromYourpkg()
}
```

```go:yourmodule/go.mod
module yourmodule

go 1.18
```

```go:yourpkg.go
package yourpkg

import (
	"fmt"
)

func HelloFromYourpkg() {
	fmt.Println("Hello from yourpkg!")
}
```

<details><summary>実行例</summary>

```:実行例
D:\mymodule>go run main.go
Hello World!
Hello from yourpkg!
```
</details>

### 補足

ローカルに配置したパッケージを`github.com`などから始めたいとき。
以下のようにパッケージ名と同じになるようにディレクトリを作成します

![h_directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/f90e0cc8-777d-4821-e097-d4cdfb6ec792.png)

mymoduleの`go.mod`を以下のように記述すると、インターネット経由でgithub.com上のパッケージをインポートするようにローカル環境のパッケージをインポートすることが出来ます。

```go:mymodule/go.mod
module mymodule

go 1.18

require github.com/yourmodule v0.0.0

replace github.com/yourmodule => ../github.com/yourmodule
```

```go:main.go
package main

import (
	"fmt"
	//モジュールからのパスでインポート
	"github.com/yourmodule/yourpkg"
)

func main() {
	fmt.Println("Hello World!")
	yourpkg.HelloFromYourpkg()
}
```

# 参考資料

- [Goのプロジェクト構成の基本](https://zenn.dev/nobonobo/articles/4fb018a24f9ee9)
- [Go言語の開発環境セットアップとサンプルプロジェクト作成](https://dev.classmethod.jp/articles/go-setup-and-sample/)
- [Custom Package in Golang — Part II](https://towardsdev.com/custom-package-in-golang-part-ii-b3aec3d7e86c)
- [Go言語のパッケージ管理方法（GOPATHモードとmodule-awareモード）について理解を深める](https://nishinatoshiharu.com/go-modules-history/)