Goでパッケージを書く方法

パッケージは、同じディレクトリに存在し、先頭に同じパッケージステートメントを持つGoファイルで構成されます。 プログラムをより洗練させるために、パッケージから追加の機能を含めることができます。 一部のパッケージはGo標準ライブラリから入手できるため、Goインストールとともにインストールされます。 その他は、Goのgo getコマンドでインストールできます。 また、必要なpackageステートメントを使用してコードを共有するディレクトリと同じディレクトリにGoファイルを作成することにより、独自のGoパッケージを構築することもできます。

このチュートリアルでは、他のプログラミングファイル内で使用するGoパッケージを作成する方法を説明します。

前提条件

  • How To Install and Set Up a Local Programming Environment for Goシリーズのチュートリアルの1つに従って、Goプログラミング環境をセットアップします。 ローカルプログラミング環境のチュートリアルのステップ5に従ってGoワークスペースを作成します。 この記事の例と命名規則に従うには、最初のセクション「パッケージの作成とインポート」をお読みください。

  • GOPATHの知識を深めるには、記事Understanding the GOPATHをお読みください。

パッケージの作成とインポート

パッケージの作成は、他のGoファイルの作成と同じです。 パッケージには、他のGoプログラムで使用できる関数、types、およびvariablesの定義を含めることができます。

新しいパッケージを作成する前に、Goワークスペースにいる必要があります。 これは通常、gopathの下にあります。 この例では、このチュートリアルではパッケージをgreetと呼びます。 これを行うために、プロジェクトスペースの下のgopathgreetというディレクトリを作成しました。 組織がgopherguidesであり、Githubをコードリポジトリとして使用しながら組織の下にgreetパッケージを作成したい場合、ディレクトリは次のようになります。

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides

greetディレクトリはgopherguidesディレクトリ内にあります。

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── greet

最後に、ディレクトリに最初のファイルを追加できます。 パッケージ内のprimaryまたはentry pointファイルは、ディレクトリの名前にちなんで名付けられるのが一般的な方法と考えられています。 この場合、greetディレクトリ内にgreet.goというファイルを作成します。

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── greet
                    └── greet.go

ファイルを作成したら、プロジェクト間で再利用または共有したいコードを書き始めることができます。 この場合、Hello Worldを出力するHelloという関数を作成します。

テキストエディタでgreet.goファイルを開き、次のコードを追加します。

greet.go

package greet

import "fmt"

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

この最初のファイルを分類しましょう。 各ファイルの最初の行には、作業しているpackageの名前が必要です。 greetパッケージを使用しているため、packageキーワードに続けてパッケージの名前を使用します。

package greet

これにより、ファイル内のすべてがgreetパッケージの一部として扱われるようにコンパイラーに指示されます。

次に、importステートメントで使用する必要のある他のパッケージを宣言します。 このファイルでは、fmtパッケージの1つだけを使用しています。

import "fmt"

最後に、関数Helloを作成します。 fmtパッケージを使用してHello, World!を出力します。

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

greetパッケージを作成したので、作成した他のパッケージで使用できます。 greetパッケージを使用する新しいパッケージを作成しましょう。

exampleという名前のパッケージを作成します。つまり、exampleという名前のディレクトリが必要です。 gopherguides組織でこのパッケージを作成すると、ディレクトリ構造は次のようになります。

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                    └── example

新しいパッケージのディレクトリができたので、エントリポイントファイルを作成できます。 これは実行可能プログラムになるため、エントリポイントファイルにmain.goという名前を付けることをお勧めします。

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── example
                    └── main.go

テキストエディタでmain.goを開き、次のコードを追加してgreetパッケージを呼び出します。

main.go

package main

import "github.com/gopherguides/greet"

func main() {
    greet.Hello()
}

パッケージをインポートしているため、パッケージ名をドット表記で参照して関数を呼び出す必要があります。 Dot notationは、使用しているパッケージの名前と、そのパッケージ内で使用したいリソースの間にピリオド.を置く方法です。 たとえば、greetパッケージには、リソースとしてのHello関数があります。 そのリソースを呼び出す場合は、greet.Hello()のドット表記を使用します。

これで、ターミナルを開いてコマンドラインでプログラムを実行できます。

go run main.go

すると、次の出力が表示されます。

OutputHello, World!

パッケージで変数を使用する方法を確認するために、greet.goファイルに変数定義を追加しましょう。

greet.go

package greet

import "fmt"

var Shark = "Sammy"

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

次に、main.goファイルを開き、次の強調表示された行を追加して、fmt.Println()関数のgreet.goから変数を呼び出します。

main.go

package main

import (
    "fmt"

    "github.com/gopherguides/greet"
)

func main() {
    greet.Hello()

    fmt.Println(greet.Shark)
}

プログラムを再度実行したら:

go run main.go

次の出力が表示されます。

OutputHello, World!
Sammy

最後に、greet.goファイルでタイプも定義しましょう。 nameフィールドとcolorフィールドを持つタイプOctopusと、呼び出されたときにフィールドを出力する関数を作成します。

greet.go

package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
    Name  string
    Color string
}

func (o Octopus) String() string {
    return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

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

main.goを開いて、ファイルの最後にそのタイプのインスタンスを作成します。

main.go

package main

import (
    "fmt"

    "github.com/gopherguides/greet"
)

func main() {
    greet.Hello()

    fmt.Println(greet.Shark)

    oct := greet.Octopus{
        Name:  "Jesse",
        Color: "orange",
    }

    fmt.Println(oct.String())
}

oct := greet.Octopusを使用してOctopusタイプのインスタンスを作成すると、main.goファイルの名前空間内のタイプの関数とフィールドにアクセスできます。 これにより、greetを呼び出さずに、最後の行にoct.String()を書き込むことができます。 たとえば、greetパッケージの名前を参照せずに、oct.Colorなどのタイプフィールドの1つを呼び出すこともできます。

OctopusタイプのStringメソッドは、fmt.Sprintf関数を使用して文を作成し、returnsは結果、文字列を呼び出し元(この場合はメインプログラム)。

プログラムを実行すると、次の出力が表示されます。

go run main.go
OutputHello, World!
Sammy
The octopus's name is "Jesse" and is the color orange.

OctopusStringメソッドを作成することにより、カスタムタイプに関する情報を印刷するための再利用可能な方法が得られます。 将来このメソッドの動作を変更する場合は、この1つのメソッドのみを編集する必要があります。

エクスポートされたコード

呼び出したgreet.goファイル内のすべての宣言が大文字になっていることに気付いたかもしれません。 Goには、他の言語のようにpublicprivate、またはprotected修飾子の概念がありません。 外部の可視性は大文字で制御されます。 大文字で始まる型、変数、関数などは、現在のパッケージの外部で公開されています。 パッケージの外側に表示されるシンボルは、exportedと見なされます。

resetと呼ばれる新しいメソッドをOctopusに追加する場合、greetパッケージ内から呼び出すことはできますが、main.goファイルから呼び出すことはできません。 t4)sパッケージ:

greet.go

package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
    Name  string
    Color string
}

func (o Octopus) String() string {
    return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

func (o *Octopus) reset() {
    o.Name = ""
    o.Color = ""
}

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

main.goファイルからresetを呼び出そうとすると、次のようになります。

main.go

package main

import (
    "fmt"

    "github.com/gopherguides/greet"
)

func main() {
    greet.Hello()

    fmt.Println(greet.Shark)

    oct := greet.Octopus{
        Name:  "Jesse",
        Color: "orange",
    }

    fmt.Println(oct.String())

    oct.reset()
}

次のコンパイルエラーが表示されます。

Outputoct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)

Octopusからreset機能をexportにするには、resetRを大文字にします。

greet.go

package greet

import "fmt"

var Shark = "Sammy"

type Octopus struct {
    Name  string
    Color string
}

func (o Octopus) String() string {
    return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}

func (o *Octopus) Reset() {
    o.Name = ""
    o.Color = ""
}

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

その結果、エラーが発生することなく、他のパッケージからResetを呼び出すことができます。

main.go

package main

import (
    "fmt"

    "github.com/gopherguides/greet"
)

func main() {
    greet.Hello()

    fmt.Println(greet.Shark)

    oct := greet.Octopus{
        Name:  "Jesse",
        Color: "orange",
    }

    fmt.Println(oct.String())

    oct.Reset()

    fmt.Println(oct.String())
}

プログラムを実行する場合:

go run main.go

次の出力が表示されます。

OutputHello, World!
Sammy
The octopus's name is "Jesse" and is the color orange
The octopus's name is "" and is the color .

Resetを呼び出すことにより、NameおよびColorフィールドのすべての情報をクリアしました。 Stringメソッドを呼び出すと、フィールドが空になっているため、NameColorが通常表示される場所には何も出力されません。

結論

Goパッケージの作成は、他のGoファイルの作成と同じですが、別のディレクトリに配置すると、コードを分離して他の場所で再利用できます。 このチュートリアルでは、パッケージ内に定義を記述する方法を説明し、別のGoプログラミングファイル内でそれらの定義を使用する方法を示し、アクセスするためにパッケージを保持する場所のオプションについて説明しました。