モジュール
モジュールは、1つのアプリやライブラリをまとめる単位です。go.modが置かれたフォルダとその中身全体が1つのモジュールになります。go.modファイルはgo mod initコマンドで生成することができます。サブディレクトリごとにgo mod initする必要はありません。同じgo.modの下にあるパッケージは、すべてこのモジュールに属します。
mkdir demo
cd demo
go mod init example.com/demomodule example.com/demo
go 1.26go行は、このモジュールが想定するGo言語のバージョンです。
外部パッケージをソースコード上でimportし、go mod tidyコマンドを実行すると、外部ライブラリを自動で検知し、go.modの依存関係を自動で更新してくれます。
パッケージ
パッケージはコンパイルの単位であり、識別子の名前空間でもあります。通常、1つのディレクトリが1つのパッケージです。同じディレクトリの.goファイルには同じpackage句を書き、まとめてコンパイルします。
// greeter/greet.go
package greeter
func Hello(name string) string {
return "hello, " + name
}// main.go
package main
import (
"fmt"
"example.com/demo/greeter"
)
func main() {
fmt.Println(greeter.Hello("Go"))
}import
package句のあと、import宣言で外部から使用したいパッケージを読み込むことができます。
import "fmt"()を使ってまとめてimportすることもできます。
import (
"errors"
myio "io"
_ "image/png"
)別名を付けると、パッケージ名の衝突を避けられます。myio "io"ならmyio.Readerのように使います。
importしたパッケージを使用しないとコンパイルエラーになります。使用はしないけどパッケージの初期化だけ実行したいときは_ "image/png"のようにブランクimportします。
import . "fmt"は、パッケージ名なしで識別子を取り込む書き方です。通常ならfmt.Printlnと書くところを、Printlnだけで呼べます。
import . "fmt"
func main() {
Println("hello") // fmt.Println("hello")と同じ
}importパスとパッケージ名
importパスは、import "..."に書く文字列です。モジュールパスにディレクトリを足したもので、どのフォルダのコードを取り込むかを指定します。パッケージ名は、取り込んだあとにコード上で使う名前です。そのディレクトリの.goファイル先頭のpackage句で決まります。
// greeter/greet.go
package greeter
func Hello(name string) string {
return "hello, " + name
}import "example.com/demo/greeter" // importするときはディレクトリ名
greeter.Hello("Go") // 呼び出すときはpackage名公開と非公開
関数や定数といった識別子の公開範囲は、名前の先頭文字で決まります。大文字で始まる名前は他パッケージから参照でき、小文字で始まる名前は同じパッケージ内だけです。
package greeter
func Hello() string { // 他パッケージから呼べる
return hello()
}
func hello() string { // 同じパッケージ内だけ
return "hello"
}import "example.com/demo/greeter"
greeter.Hello()
// greeter.hello() // コンパイルエラー: 小文字は他パッケージから見えないinternalパッケージ
パスにinternalを含むパッケージは、そのinternalの親ディレクトリ以下からだけimportできます。
たとえばexample.com/demo/internal/storeは、example.com/demo/...配下からはimportできますが、別モジュールやexample.com/demoの外にあたるパスからはimportできません。公開したくない実装を内側に閉じるための仕組みです。
ドキュメントコメント
ドキュメントコメントは、トップレベルのpackage、const、var、type、funcの直前に、空行を挟まず書いたコメントです。関数内の変数や、関数の引数・戻り値名には、ドキュメントコメントはありません。
// ParseConfigは設定ファイルを読み込む。
func ParseConfig(path string) (*Config, error) {
// ...
}コメントと宣言のあいだに空行があると、ドキュメントにはなりません。
// このコメントはドキュメントにならない
func other() {}constやvarをまとめたときは、グループ全体へのコメントと、各識別子の行末コメントが使えます。構造体のフィールドとインターフェースのメソッドも、型のドキュメントの一部になります。
// Statusは処理の状態です。
type Status int
const (
Ready Status = 1 // 準備完了
Running Status = 2 // 実行中
)
// Serverは待ち受け設定です。
type Server struct {
Host string // 待ち受けホスト
Port int // 待ち受けポート
}init関数
パッケージ内にfunc init()を複数定義できます。initはインポートや変数の初期化のあと、自動的に実行されます。
func init() {
log.SetFlags(log.LstdFlags)
}パッケージの初期化順序
パッケージの初期化は次の順序です。
- インポートされたパッケージ(依存関係の順)
- パッケージレベルの変数(宣言順)
init関数(ソースファイル内の出現順)
フォーマット
Go言語のソースコードは、手で細かく揃えるのではなくgofmt(パッケージ単位ならgo fmt)で機械的に整形することができます。インデントはタブ、空白の入れ方や並びも標準形に揃います。
ファイルをその場で書き換える例です。
gofmt -w main.goパッケージ単位で揃えるときはgo fmtを使います。結果のスタイルはgofmtと同じです。
go fmt ./...テスト
テストは、対象と同じディレクトリに_test.goで終わるファイルを置きます。書き方は2通りあり、非公開の識別子をテストできるかどうかが異なります。
同じpackage greeterで書くと、非公開の識別子も含めパッケージ内部がすべて見えます。非公開の補助関数や内部状態まで検証したいときに使います。importは不要で、同じパッケージの関数をそのまま呼べます。
// greeter/greet_test.go
package greeter
import "testing"
func TestHello(t *testing.T) {
if Hello("Go") != "hello, Go" {
t.Fatal("unexpected greeting")
}
}package greeter_testで書くと、他パッケージからの利用と同じです。importが必要で、公開されている識別のみテストできます。
// greeter/greet_ext_test.go
package greeter_test
import (
"testing"
"example.com/demo/greeter"
)
func TestHello(t *testing.T) {
if greeter.Hello("Go") != "hello, Go" {
t.Fatal("unexpected greeting")
}
// greeter.hello() // コンパイルエラー: 他パッケージからは見えない
}