Go 言語でパッケージを自作して、それを呼び出してみる – その1

はじめに

前回は、外部モジュールの呼び出し方 を学びました。

世界中の優秀なエンジニアの方々が作成したモジュールを利用できれば、できることの幅が随分広がります。

そんな便利な部品 モジュール を自作してみようというのが今回のエントリです。

検証環境

$ head -n 2 /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"

$ go version
go version go1.16.2 linux/amd64

学習のゴール

今回+次回のエントリでの学習のゴールは、2 つのモジュールを作成するところまでとします。

  1. 1 つ目は他のライブラリまたはアプリケーションからインポートされることを目的とした ライブラリの作成
  2. 2 つ目は先に作成したライブラリを利用する アプリケーションの作成

今回エントリは 1 のライブラリの作成までとします。
次回エントリで 2 を進めていきます。

モジュール作成の前に用語や概念を整理をしておきます

用語が増えだしたので、自作モジュールの作成の前に頭の中を整理しておきたいと思います。

チュートリアルを読む限りだと、「ライブラリ」と「アプリケーション」いずれも 「モジュール」 と呼ぶようです。

モジュール の中には複数の「ディレクトリ」が含まれています。
「ディレクトリ」の中には複数の「Go コード」が含まれていますが、ディレクトリに含まれている「Go コード」のパッケージは必ず同一でなければいけません。

モジュールの中には機能や特徴が関係している関数をグループ化して入れておきます。
全く関係のないものを寄せ集めてモジュール化するのは、他の人が理解しにくくなるのでやめておいた方がいいでしょう。

  • モジュール : パッケージをグループ化したもの。
  • パッケージ : Go 言語 のソースコードをグループ化したもの。ディレクトリと考えてよさそう。
  • ソースコード : 関数 ( や構造体、インターフェイス ) を記述したもの。

また Java の経験者からは違和感があると思いますが、ディレクトリ名とパッケージ名は一致する必要はありませんし、ディレクトリ階層とパッケージ階層も一致する必要はありません。
ただし、できればディレクトリ名とパッケージ名は一致するようにしたほうがよいようです。必然的にパッケージ名にはディレクトリ名に利用できないような文字列は利用されなくなります

ここまでで理解できた概念を構造化すると以下のようになります。

  • モジュール ( ライブラリ OR アプリケーション )
    • ディレクトリ 1 ( package 1 )
    • a.go
    • b.go
    • ディレクトリ 2 ( package 2 )
    • c.go
    • d.go
    • ディレクトリ 3
    • ディレクトリ 4 ( package 3 )
      • e.go

ようやく少し用語が整理できてきました。

上記を踏まえて、学習に進みます。

学習の流れ

7 つの工程を経て、自作モジュールの作成を学習していきます。
それぞれの工程で、できるかぎり Go 言語の特徴に触れていきます。
今回だけで紹介しきれないため、次のエントリでも引き続き紹介していきます。

  1. モジュールの作成
    • モジュールをコーティングします。モジュールの中に関数を配置します。
  2. 作成したモジュールを他のモジュールから呼び出す
    • 作ったモジュールの関数を、他のモジュールから呼び出してみます。
  3. エラーの返却
    • 単純なエラーを発生させ、それを呼び出し元でハンドリングしてみます。
  4. ランダムに挨拶文を返却させてみる
    • slice ( Go 言語 の動的なサイズの配列 ) としてデータを返却し操作します
  5. 複数の人々に挨拶文を返却する
    • map を使って key-value の値を作成します
  6. テストコードを追加
    • 作成したコードをテストするために、ビルドインのユニットテスト機能を使ってみます
  7. アプリケーションをコンパイルし、インストールする
    • ローカル PC 内で自分のコードをコンパイルし、インストールしてみます

前提条件・推奨条件

Go 言語 のチュートリアルを進めるにあたって、以下の事項を満たしておくことを推奨します。

  1. Go 言語 以外のプログラミング経験
    • チュートリアルで記述することは非常にシンプルですが、いくつかの関数は C 言語や Java など他のプログラミング言語でも同様の機能を持っているものが登場します。
    • 他のプログラミング言語経験したことがあれば理解が容易になります
  2. 使い慣れたテキストエディタ
    • 多くの適正化が Go 言語 をサポートしています。例えば VSCode 、 GoLand それから Vim。
  3. 使い慣れたターミナル.
    • 使い慣れたターミナルがあればそちらを使いましょう。
    • Linux macOS であれば標準の Terminal を使うことも出来ます。 Windows であれば PowerShell や cmd を利用できます。

モジュール作成開始!

それではいよいよ Go 言語 のモジュールを作成してみます。
他の開発者も利用できるモジュールを作ってみましょう、

自分が作成するモジュールには、そのモジュールが利用する以下の情報を記述しておく必要があります。

  • Go のバージョン
  • 依存モジュールのリスト

モジュールに新しい機能を追加したり、直したりした場合は、 新しいバージョンのモジュール として公開します。
僕たちの作ったモジュールが、意図せず最新のものに変わってしまった場合、利用者は当惑するはずです。
明示的にバージョンアップをすることで、このような混乱を避けることができます。
モジュールのバージョンが上がったとしても、利用者が自分でバージョンアップを行わない限りは自動的にバージョンが上がることは有りません。

コーディング開始!

それではコーディングを進めていきます。
以下の手順を進めていけば大丈夫です。

1. ターミナルと開きます

2. ターミナルに以下のように入力します

ホームディレクトリに移動しましょう。

$ cd

Windows OS の場合には以下のコマンドを入力します。

% cd %HOMEPATH%

3. ソースコード配置ディレクトリを作成し、移動します

# greetingsディレクトリを作成
mkdir greetings

# 作成したディレクトリに移動
cd greetings

4. モジュールの作成を開始します。

go mod init コマンドを実行します。

こちらも前回のブログエントリで実行したコマンド になります。

アプリケーションを作る場合またはライブラリを作る場合のどちらの場合でも、 go mod init は必要ということですね。

前回実行した時 と同様に、 モジュールの名前 を引数として渡します。
モジュール名前は github のリポジトリ名をイメージして命名 しましょう。

僕の場合だと genzouw/greeting というリポジトリ名が良いかと思います。

$ go mod init genzouw/greetings
go: creating new go.mod: module genzouw/greetings

コマンドを実行すれば go.mod というファイルがディレクトリに作成されます。
この時点で go.mod ファイルには、以下の 2 つの情報だけが書かれているです。

  • モジュールの名前
  • モジュールがサポートする go のバージョン
$ cat go.mod
module genzouw/greetings

go 1.16

5. テキストエディタを開いて greetings.go というファイルを作成します。

ex: vi greetings.go

6. greetings.go に以下のコードを貼り付けて保存します

package greetings

import "fmt"

// Hello returns a greeting for the named person.
func Hello(name string) string {
    // Return a greeting that embeds the name in a message.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message
}

これが今回作成するモジュールの一番最初のコードです。
この関数は呼び出し元に挨拶文を返します。

次のステップではこの関数を呼び出すアプリケーションをコーディングします。

このソースコードは、以下のようなことを行っています。

  • greetings という名前のパッケージを宣言
  • 挨拶文を返却する Hello という名前の関数を実装
    • この関数は name という名前のパラメーターを 1 つ受け取ります。
    • name パラメーターは string 型です。
    • またこの関数は string 型の値を 1 つ返却します。

さらに関数の名前に特徴があります。
Go 言語 では先頭の 1 文字目が大文字で始まる関数は、同じパッケージ以外からも呼び出すことができます。
逆に言うと名前が小文字で始まる関数は同じパッケージ内からしか呼び出すことができません。

この仕組みは Go 言語 の中では exported name と呼ばれています。

  • message という名前の変数を宣言し挨拶文を格納しています。
    • Go 言語 では := 演算子は、 変数の宣言と代入を行うためのシンタックスシュガー となっています。
    • 変数を宣言と同時にできるだけ初期化したほうがにくいので頻繁に利用する演算子だと思います。
    • この演算子を使わない場合は、以下のようにあえて冗長に書くこともできます。
var message string
message = fmt.Sprintf("Hi, %v. Welcome!", name)
  • 挨拶文を生成するために fmt パッケージの Sprintf 関数を利用しています。
    • 関数の第 1 引数は string 型でフォーマットを指定しています。
    • %v の部分は変数に置き換えられます。
    • 最後に生成された挨拶文を呼び出し元に返却します。

本日はここまでです。

ひとこと

実行できなかったため、本当に正しくコーディングできたのか不明のままで気持ち悪さが残ります。

次のステップでは、 作成した関数を別のモジュールから呼び出してみたい と思います。

Posted by genzouw