[Golang] Package

2021-11-05 hit count image

GolangでPackage(パッケージ)について詳しく説明して、使う方法についても説明します。

概要

今回のブログポストではGolangのPackage(パッケージ)を詳しく説明して、使う方法についても説明します。このブログポストで紹介するコードは次のリンクで確認できます。

パッケージ

Golangでパッケージはコードをまとめる基本単位です。したがって、全てのコードは必ずパッケージ中に入れる必要があります。

また、Golangでプログラムを作成する場合、必ずmainパッケージが存在する必要があり、mainパッケージ中にmain関数が定義される必要があります。Golangではこのmainパッケージのmain関数がプログラムのエントリーポイントとなります。

mainパッケージ以外のパッケージはエントリーポイント(main関数)がないパッケージで、mainパッケージの補助パッケージで使います。

このように全てのパッケージを持ってるものがモジュールです。モジュールについては下記のリンクを参考してください。

パッケージの使い方

そしたら、パッケージを使う方法について確認するため、main.goファイルを次のように修正します。

package main

import (
  "fmt"
  "math/rand"
)

func main() {
  fmt.Println(rand.Int())
}

この例題ではfmtパッケージとmathパッケージの配下のrandパッケージを持ってきて、画面にランダムな数字を表示します。このように作成したコードを実行すると、次のような結果が表示されます。

# go run main.
5577006791947779410

パッケージの外部共有

Golangではパッケージ内のタイプ、グローバル変数、定数、関数、メソードなどを外部に公開して他のパッケージで使うことができます。パッケージで外部で公開したいものは大文字で始まって、外部で公開しないものは小文字で始まります。

パッケージを外部で公開して公開されたパッケージを使う時にはモジュールを使う必要があります。モジュールについて詳しい内容は下記のリンクを参考してください。

外部で公開するパッケージを作ってみるため、greetingフォルダを作ってgreeting.goファイルを生成して次のように修正します。

package greeting

import "fmt"

const PI = 3.14

func PrintGreeting() {
  print()
}

func print() {
  fmt.Println("Hello, World!")
}

このように修正したら、次のコマンドを使ってgreetingモジュールを生成します。

go mod init github.com/dev-yakuza/study-golang/package/greeting

このgreetingパッケージはgreetingモジュールを通じて共有される予定です。このように共有されたgreetingパッケージを使うmainパッケージを作ってみましょう。

greetingフォルダと同じ位置にmainフォルダを生成して当該フォルダにmain.goファイルを作って次のように修正します。

package main

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/package/greeting"
)

func main() {
  fmt.Println(greeting.PI)
  greeting.PrintGreeting()
}

次は下記のコマンドを使ってモジュールを生成します。

go mod init github.com/dev-yakuza/study-golang/package/main

モジュールを生成したら、ローカルに存在するgreetingモジューロを使うため下記のコマンドを実行します。

go mod edit -replace github.com/dev-yakuza/study-golang/package/greeting=../greeting

最後の次のコマンドを実行してモジュールを設定します。

go mod tidy

このように作ったプログラムを実行すると次のような結果を確認できます。

# go run main.go
3.14
Hello, World!

init関数

パッケージはinit関数を定義することが出来て、init関数はパッケージがimportされる時1回コールされます。パッケージのinit関数はパッケージ内のグローバル変数を初期化する時使います。

これを確認するためgreeting/greeting.goファイルを次のように修正します。

package greeting

import "fmt"

const PI = 3.14

var globalVal = 0

func init() {
  fmt.Println("init()", globalVal)
  globalVal++
}

func PrintGreeting() {
  print()
}

func print() {
  fmt.Println("Hello, World!", globalVal)
}

そしてmainフォルダで下記のコマンドを実行して修正した内容を確認します。

go run main.go

プログラムが実行されると次のような結果が表示されます。

init() 0
3.14
Hello, World! 1

エイリアスパッケージ名

Golangではこのように既に作られったパッケージを使ってプログラムを作成します。しかし、全ての開発者が約束をしてパッケージを作成するわけではないので、たまにパッケージ名が重複される時があります。


import (
  "text/template"
  "html/template"
)

template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)
template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)

このように重複されるパッケージ名を使う場合、エイリアスパッケージ名(Alias Package Name)を使います。


import (
  htmlplate "html/template"
  "text/template"
)

template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)
htmlplate.New("foo").Parse(`{{define "T"}}Hello{{end}}`)

これを確認するためmain.goファイルを下記のように修正します。


package main

import (
  "fmt"
  htmlplate "html/template"
  "text/template"
)

func main() {
  fmt.Println(template.New("foo").Parse(`{{define "T"}}Hello{{end}}`))
  fmt.Println(htmlplate.New("foo").Parse(`{{define "T"}}Hello{{end}}`))
}

これを実行すると次のような結果が表示されます。

# go run main.go
&{foo 0xc00010e480 0xc00006c050  } <nil>
&{<nil> 0xc000104140 0xc00010e900 0xc00006c0a0} <nil>

空白識別子

Golangではパッケージも変数と同じように宣言したら必ず使う必要があります。そしないと、コンパイルエラーが発生します。しかし、パッケージを直接使わないけど、importをしてinit()関数を実行して、パッケージを初期化する場合があります。この時、エイリアスパッケージ名空白識別子(Blank identifier, _)を使ってエラーを解決することが出来ます。

import (
  "database/sql"
  _ "github.com/mattn/go-sqlite3"
)

パッケージをimportすると基本的パッケージ中のinit()関数がコールされます。つまり、パッケージをimportしてinit()関数をコールする必要はありますが、他の機能を使わない時、上のようにエイリアスパッケージ名と空白識別子を活用します。

これを確認するためinit関数の例題でmain.goファイルを次のように修正します。

package main

import (
  "fmt"

  _ "github.com/dev-yakuza/study-golang/package/greeting"
)

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

これを実行すると下記のような結果が表示されます。

# go run main.go
init() 0
Hello

import cycle

Golangでは異なるパッケージがお互いimportをするとimportが繰り返されため、import cycleエラーが発生します。

これを確認するためgreeting/greeting.goファイルを生成して、次のように修正します。

package greeting

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/package/name"
)

func Print() {
  fmt.Println("Hello, ", name.Name)
}

次は下記のコマンドを実行してモジュールを生成します。

# cd greeting
go mod init github.com/dev-yakuza/study-golang/package/greeting

次はimport cycleエラーを発生させるため、greetingフォルダと同じ位置にname/name.goファイルを生成して、次のように修正します。

package name

import (
  "github.com/dev-yakuza/study-golang/package/greeting"
)

var Name = "John"

func print() {
  greeting.Print()
}

このように生成したnameパッケージも次のコマンドを実行してモジュールを生成します。

# cd name
go mod init github.com/dev-yakuza/study-golang/package/name

このように作ったそれそれのモジュールをお互いに参照するモジュールを連結するため次のコマンドを実行します。

cd greeting
go mod edit -replace github.com/dev-yakuza/study-golang/package/name=../name
go mod tidy
cd ..
cd name
go mod edit -replace github.com/dev-yakuza/study-golang/package/greeting=../greeting
go mod tidy

このように設定をしたら、name.goファイルを開いてgreetingパッケージを追加するところを見ると、次のようにimport cycle not allowedエラーが発生することが確認できます。

import cycle not allowed

完了

これでGolangでパッケージが何か、どう使うのかについてみてみました。これからパッケージを作って外部に共有したら、共有された外部パッケージを使ってプログラムを作成してみてください。

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

Posts