前回の内容
前回実施した内容を理解した前提なので、まずは下記をやってみてくださいね
今回実施する内容
このチュートリアルでは、2 つのモジュールを作成します。
実施するチュートリアルは下記になります
公式ドキュメントの冒頭の説明
1 つ目は、他のライブラリまたはアプリケーションによってインポートされることを意図したライブラリです。2 番目は、最初のアプリケーションを使用する呼び出し側アプリケーションです。
このチュートリアルのシーケンスには、それぞれ言語の異なる部分を説明する 7 つの簡単なトピックが含まれています。
- モジュールを作成する — 別のモジュールから呼び出すことができる関数を含む小さなモジュールを作成します。
- 別のモジュールからコードを呼び出す— 新しいモジュールをインポートして使用します。
- エラーを返して処理する— 簡単なエラー処理を追加します。
- ランダムな挨拶を返す— データをスライス (Go の動的サイズの配列) で処理します。
- 複数の人に挨拶を返す — キーと値のペアをマップに保存します。
- テストを追加— Go の組み込みの単体テスト機能を使用して、コードをテストします。
- アプリケーションをコンパイルしてインストールする — コードをローカルでコンパイルしてインストールします。
Go モジュールを作成することから始めます。モジュールでは、個別の便利な機能セットに関連する 1 つ以上のパッケージを収集します。たとえば、財務分析を行うための機能を備えたパッケージを含むモジュールを作成して、他の財務アプリケーションを作成しているユーザーが自分の作業を使用できるようにすることができます。モジュールの開発について詳しくは、モジュールの 開発と公開を参照してください。
全体像が見えたところで、実際にやってみよう
モジュールを作成する
プロジェクトの作成するイメージでモジュールを開始します。
今回はgreentingというモジュールになります。
// ディレクトリの作成
$mkdir greeting
$cd greeting
// go.mod作成
$go mod init example.com/greeting
go: creating new go.mod: module example.com/greeting
前回同様にこれでgo.modの生成まで終えました
module example.com/greeting
go 1.19
greetings.goを作成して、型定義を理解する
greetings.goを作成して、挨拶する処理を作成していきましょう。
関数の定義は下記の構成になっております
func 関数名(引数 型定義) 返り値の型 {
// 処理内容
}
実際に記述してみます
package greetings
import (
"fmt"
)
// Hello は、指定された人に対する挨拶を返す
func Hello(name string) string {
// メッセージに名前を埋め込んだグリーティングを返す。
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message
}
型はJavaっぽいですね
これは、モジュールの最初のコードです。あいさつを求める発信者には、あいさつを返します。次の手順で、この関数を呼び出すコードを記述します。
この関数は、
Go公式name
がstring
型のパラメータをとり、string
を返します。Go では、名前が大文字で始まる関数は、同じパッケージにない関数から呼び出すことができます。これは、Go ではエクスポートされた名前として知られています。エクスポートされた名前の詳細について は、Go ツアーの エクスポートされた名前を参照してください。
どういうことだろう?
このモジュールをimportしても、小文字から始まる関数は実行できない仕様です。
大文字なら外部モジュールでも実行できます
この辺りは下記の記事を読んでみるとわかりやすいです。
小文字だとprivateメソッドの扱いですね
この辺りは下記のページの説明をみてみるとよいでしょう
Goでは、最初の文字が大文字で始まる名前は、外部のパッケージから参照できるエクスポート(公開)された名前( exported name )です。 例えば、
https://go-tour-jp.appspot.com/basics/3Pi
はmath
パッケージでエクスポートされています。
小文字ではじまるpi
やhoge
などはエクスポートされていない名前です。
パッケージをインポートすると、そのパッケージがエクスポートしている名前を参照することができます。 エクスポートされていない名前(小文字ではじまる名前)は、外部のパッケージからアクセスすることはできません。
関数がエクスポートされないということなので、JavaScriptと似たイメージですね。
オンラインで動作確認できるので、ありがたいです
関数はmainから開始
A Tour of Goでも説明されていますが、モジュール内で作成されたファイルつまりパッケージは、main()
から実行されます。
Goのプログラムは、パッケージ( package )で構成されます。
プログラムはmain
パッケージから開始されます。
このプログラムでは"fmt"
と"math/rand"
パッケージをインポート( import )しています。
規約で、パッケージ名はインポートパスの最後の要素と同じ名前になります。 例えば、インポートパスが"math/rand"
のパッケージは、package rand
ステートメントで始まるファイル群で構成します。
main()から実行されるということは、まずはmain()から読めばいいので、可読性もよいですね
言語で統一されているのはありがたいですね
変数の型定義は:=で省略可能
message := fmt.Sprintf("Hi, %v. Welcom!", name)
変数定義時の:=はなんだろう?
:=
で変数名を定義すると、変数の型を自動的に定義してくれます
=
で定義すると下記のようになります
// 変数をstring型で宣言
var message string
// 変数に値を割り当てる
message = fmt.Sprintf("Hi, %v. Welcome!", name)
このように、変数を宣言してから、値を割り当てます。
JavaとかTypeScriptでよく使いますよね
しかし、:=
を利用すること変数の宣言と同時に、値を割り当てることができます
message := fmt.Sprintf("Hi, %v. Welcom!", name)
2行だった箇所が1行にまとまり、スッキリしました
別のモジュールからコードを呼び出す
構成
<HelloPkgs>/
|-- greetings/
|-- hello/
helloモジュールを作成する
$cd ../
$mkdir hello
$cd hello
$go mod init example.com/hello
go: creating new go.mod: module example.com/hello
package main
import (
"fmt"
"example.com/greetings"
)
func main() {
message := greetings.Hello("Gladys")
fmt.Println(message)
}
go mod tidyを実行して、モジュールをimportしてみます
$ go mod tidy
go: finding module for package example.com/greetings
example.com/hello imports
example.com/greetings: cannot find module providing package example.com/greetings: unrecognized import path "example.com/greetings": reading https://example.com/greetings?go-get=1: 404 Not Found
404 Not Found
が表示されて、importに失敗しましたね
これは先ほど作成したgreetingsのmodが公開されていないため、go mod tidy
を実行してもモジュールを見つからずエラーになるのです。
そこで、まだモジュールが公開されていない場合は、ローカルのディレクトリパスを指定します。
ローカルのモジュールをimportする
go.mod内でreplaceを利用してローカルパスを指定し、require時の仮バージョンを設定します。
これまではモジュールとバージョンを指定する形式でした。
require モジュール vバージョン
require
を利用しているのが特徴です。
前回やった例だと下記ですね
module example/hello
go 1.19
require rsc.io/quote v1.5.2 // バージョンを指定
require (
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/sampler v1.3.0
)
しかし、今回は未公開のローカルにあるモジュールを利用するため、replace
で指定してあげます
replace モジュール => パス
実際にやってみましょう。
hello/go.modにgreetingsディレクトリを指定します。
go mod edit -replace example.com/greetings=../greetings
するとreplaceが記述されました
module example.com/hello
go 1.16
replace example.com/greetings => ../greetings
依存関係を記載できたので、go mod tidy
を実行してみましょう
$go mod tidy
go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000
仮のバージョンが指定されていますね
hello/go.modのexample.com/greetings
にも仮のバージョンが指定されています。
module example.com/hello
go 1.19
replace example.com/greetings => ../greetings
require example.com/greetings v0.0.0-00010101000000-000000000000 // 記載される
公式を確認すると、下記の内容が記載されています
最後に実行して、動作確認をしてみましょう!
$ go run .
Hi, Gladys. Welcom!
無事に動作されました!!
エラーを出してみる
実はここまでの作業でエラーが出て、困っていたところがあったので、
あえてエラーを出して理解を深めていきたいと思います
ローカルのモジュールが見つからない
先ほどローカルのモジュールをimportして利用しました。
しかし、下記のエラーが表示されていたらどうでしょうか?
could not import example.com/greetings (no required module provides package "example.com/greetings")
// example.com/greetings をインポートできませんでした (パッケージ "example.com/greetings" を提供する必須モジュールがありません)
helloモジュールを実行してみると下記が表示されます
$ go run .
# example.com/hello
./hello.go:5:5: imported and not used: "example.com/greetings" as greeting
./hello.go:9:16: undefined: greetings
example.com/greetingsはimportされていますが、利用されていないと表示されています。
そして、undefined: greetings
と表示され、greetingsが未定義であるとわかります。
どこでバグったかな?
ヒントは"example.com/greetings" as greeting
です。
module example.com/greeting // greetingsになっていない
go 1.19
あ、
greetingsをgreetingと間違えている
greetings/go.modを修正して、実行します。
$go run .
# example.com/hello
./hello.go:6:2: imported and not used: "example.com/greetings" as greeting
./hello.go:10:13: undefined: greetings
ですが、改善されていません。
他も修正しないといけないのかな?
そこでpackageを確認します。
package greeting
import (
"fmt"
)
func Hello(name string) string {
message := fmt.Sprintf("Hi, %v. Welcom!", name)
return message
}
package名がgreetings
ではなく、greeting
になっていました。
greetings
に修正すると、正しく実行されました。
$ go run .
Hi, Gladys. Welcom!
どっちが問題だったのだろうか?
先ほどgo.mod
とgreetings.go
を修正しました。
go.modでgreeting
に変更してみても問題なく実行することができました。
module example.com/greeting
go 1.19
$ go run .
Hi, Gladys. Welcom!
一方で、greetings.goのパッケージを間違えると失敗します
package greeting // 複数形にしない
import (
"fmt"
)
func Hello(name string) string {
message := fmt.Sprintf("Hi, %v. Welcom!", name)
return message
}
$go run .
# example.com/hello
./hello.go:6:2: imported and not used: "example.com/greetings" as greeting
./hello.go:10:13: undefined: greetings
エラーが再現できました。
そのため、上記のエラーが表示された場合は、packageから探っていくとよいでしょう。
エラーを出すと理解が深まっていいですね
次の記事
ここまでお疲れ様でした
次の記事ではエラーハンドリングを学習していきましょう
コメント