🔰

Go 言語を公式チュートリアルで始めてみる 2 (Tutorial: Create a module)

2022/10/09に公開

記事概要

Go入門者が公式チュートリアルのTutorial: Create a moduleを実施した時の手順と
調べたことを備忘録として残したものです。

Go 言語を公式チュートリアルで始めてみる 1 (Tutorial: Get started with Go)の続きです。

リポジトリはこちら
https://github.com/harusame0616/go-lang-official-tutorial


挨拶モジュール作成

挨拶モジュール用フォルダ作成と移動。

> mkdir greetings && cd greeetings

モジュールの初期化

> go mod init example.com/greetings

greetings.go作成

package greetings

import "fmt"

func Hello(name string) string {
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}
  • 大文字で始まる関数はエクスポートされ、パッケージ外部から呼び出せる関数となる。
  • := は変数の定義と初期化を同時にするショートカット。
    • 変数を定義する場合は明示的に型を指定して宣言する必要があるが、:= を使うことで代入値によって型を推論してくれる。
      通常の変数定義

        var number int
        number = 5
      

      :=による変数定義

        number := 5
      

メインモジュール作成

メインモジュールフォルダ作成と移動。

> cd .. && mkdir hello && cd hello

メインモジュールの初期化。

> go mod init example.com/hello

hello.goの作成

package main

import (
    "fmt"

    "example.com/greetings"
)

func main() {
    message := greetings.Hello("Gladys")
    fmt.Println(message)
}

メインモジュール実行

挨拶モジュールは実際に公開しているわけではないので、そのままではexample.com/greetings の依存関係を解決できない。
example.com/greetings はローカルの挨拶モジュールを参照するように変更する。

> go mod edit -replace example.com/greetings=../greetings

依存関係更新

> go mod tidy

メインモジュール実行

> go run .
Hi, Gladys. Welcome!

無事表示されました!


エラーハンドリング

greetrings.go にバリデーションを追加。

package greetings

import (
    "errors"
    "fmt"
)

func Hello(name string)(string, error) {
    if name == "" {
        return "", errors.New("empty name")
    }

    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}

hello.goでエラーが発生するように変更するのとエラーチェックの処理を追加する。

package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	message, error := greetings.Hello("")
	if error != nil {
		log.Fatal(error)
	}

	fmt.Println(message)
}
  • log.SetFlagsでログ出力する内容を変更できる。
    • eg. ファイル名・行番号(Log.Lshortfile)とローカルタイムゾーン(Log.Ldate)を指定する。
      log.SetFlags(log.Lshortfile | log.Ldate)
    • https://pkg.go.dev/log#pkg-constants
  • log.Fatal はログを出力して exit status 1 (異常終了)でプログラムを終了する。

動的な配列(slice)を使って定義済みのフォーマットからランダムなメッセージを返す

greetings.goのHelloでランダムなメッセージを返すように変更。

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
	"time"
)

func Hello(name string) (string, error) {
	if name == "" {
		return "", errors.New("empty name")
	}

	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func randomFormat() string {
	formats := []string{
		"Hi, %v. Welcome",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	return formats[rand.Intn(len(formats))]
}
  • randomFormatは小文字から始まるので外部に公開されない(パッケージ内スコープ)

  • init関数はプログラム起動時に自動で呼び出される。

  • 配列は要素数が固定の型でサイズの変更はできない。

    var numbers [5]int
    numbers = append(numbers, 9) // エラー
    
  • スライスでは配列のサイズを動的変更できる。

    var numbers []int
    numbers = append(numbers, 9) // OK
    

Mapを使って複数人にメッセージを返す。

複数の名前を取得して名前ごとにメッセージを作成するHellos関数を作成する。

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
	"time"
)

func Hello(name string) (string, error) {
	if name == "" {
		return "", errors.New("empty name")
	}

	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

func Hellos(names []string) (map[string]string, error) {
	messages := make(map[string]string)

	for _, name := range names {
		message, err := Hello(name)
		if err != nil {
			return nil, err
		}

		messages[name] = message
	}

	return messages, nil
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func randomFormat() string {
	formats := []string{
		"Hi, %v. Welcome",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	return formats[rand.Intn(len(formats))]
}
  • 配列の要素分ループするには for … range 構文を使う
  • _(空白識別子) は使わない戻り値を安全に破棄するために使う。
  • make は指定した型のオブジェクトを割り当て初期化する。

みんな大好きテスト

挨拶モジュールをテストする greetings_test.go を作成する

package greetings

import (
	"regexp"
	"testing"
)

func TestHelloName(t *testing.T) {
	name := "Gladys"
	want := regexp.MustCompile(`\b` + name + `\b`)
	msg, err := Hello("Gladys")
	if !want.MatchString(msg) || err != nil {
		t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
	}
}

func TestHelloEmpty(t *testing.T) {
	msg, err := Hello("")
	if msg != "" || err == nil {
		t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
	}
}
  • testing は go標準のテスティングパッケージ。
  • _test.goで終わるファイルをテストが含まれているファイルとして認識する。
  • テスト名は Test[Name] の形式でつける。
  • テストパッケージへのポインタをパラメータとして受け取る。

テストを実行する

> go test
PASS
ok      example.com/greetings   0.002s
  • _test.goで終わるファイル内のTestName形式の関数をテストする。
  • -v オプションで詳細表示

ビルド & インストール

ビルド時して実行

> go build
> ./hello
map[Darrin:Hi, Darrin. Welcome Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]

インストールして実行

> go install
> cd ~/ && hello # インストールされているので他のフォルダからでも呼び出せる
map[Darrin:Hi, Darrin. Welcome Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]

以上で Tutorial: Create a module は終わりとなります。
お読みいただいてありがとうございました。

お気づきの点等ありましたらコメントお願いします。

GitHubで編集を提案

Discussion