はじめに
今回はGo
言語を入門してみるという記事になります。
一応簡単なREST API
を実装するあたりまでは書いていきたいと思ってはいます。
またあくまで私の備忘録的な意味合いで記述していくので、ちゃんと学びたい方は公式ドキュメントを見るのが一番です。
インストール
インストーラーのダウンロード
以下のサイトを開き、1. Go download
にあるインストーラーのダウンロードボタンを押します。
go.dev
インストール
インストーラーを起動し、指示に従ってください。
正しくインストールすることができれば、コマンドプロンプトやターミナル等で以下のコマンドが動くはずです。
$ go version
Hello, Worldをしてみる
プログラムの最初といえばHello, World
ですよね。
go.modについて
Go
言語のコードを記述する作業フォルダのルートディレクトリにはgo.mod
という依存するモジュールを記述するUTF-8
のテキストファイルを定義する必要があります。
A module is defined by a UTF-8 encoded text file named go.mod in its root directory. The go.mod file is line-oriented. Each line holds a single directive, made up of a keyword followed by arguments.
// DeepL翻訳
モジュールは、ルートディレクトリにあるgo.modというUTF-8でエンコードされたテキストファイルで定義されます。go.mod ファイルは行指向です。各行は、キーワードと引数で構成される一つのディレクティブを保持します。
Go Modules Reference - The Go Programming Language
// go.modのサンプル module example.com/my/thing go 1.12 require example.com/other/thing v1.0.2 require example.com/new/thing/v2 v2.3.4 exclude example.com/old/thing v1.2.3 replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5 retract [v1.9.0, v1.9.5]
記法に関しては各々で調べてほしいですが、module
・go
・require
・replace
・exclude
・retract
は予約語のようですね。
誰も伝わらないと思いますが、Unity
で言うところのmanifest.json
に近いものを感じますね。
またモジュールってそもそもなんやねんとなりそうですが、公式ドキュメントには以下のように記述されています。
A module is a collection of packages that are released, versioned, and distributed together. Modules may be downloaded directly from version control repositories or from module proxy servers.
A module is identified by a module path, which is declared in a go.mod file, together with information about the module’s dependencies. The module root directory is the directory that contains the go.mod file. The main module is the module containing the directory where the go command is invoked.
Each package within a module is a collection of source files in the same directory that are compiled together. A package path is the module path joined with the subdirectory containing the package (relative to the module root). For example, the module "golang.org/x/net" contains a package in the directory "html". That package’s path is "golang.org/x/net/html".
// DeepL翻訳
モジュールは、リリースされ、バージョン管理され、一緒に配布されるパッケージの集合体です。モジュールは、バージョン管理リポジトリから直接ダウンロードすることもできますし、モジュールプロキシサーバーからダウンロードすることもできます。モジュールは、モジュールの依存関係の情報とともに go.mod ファイルで宣言されるモジュールパスによって識別されます。モジュールルートディレクトリは、go.mod ファイルを含むディレクトリです。メインモジュールは、go コマンドが起動されるディレクトリを含むモジュールです。
モジュール内の各パッケージは、同じディレクトリにあるソースファイルをコンパイルしたものです。パッケージのパスは、モジュールのパスにそのパッケージを含むサブディレクトリを加えたものです(モジュールルートからの相対パス)。例えば、"golang.org/x/net "というモジュールは、"html "というディレクトリにパッケージを含んでいます。そのパッケージのパスは「golang.org/x/net/html」です。
go.modを生成する
というわけでgo.mod
を作成していきましょう。
正直そこまで記述量があるわけではないので自力でファイルを生成して記述しても良いとは思いますが、一応コマンドで生成することができます。
$ cd 作業したいフォルダ $ go mod init example/hello
example/hello
はサンプルに記述されていたものをそのまま使ってます。別に他の好きな文字列でも構いません。
無事にコマンドが実行できるとgo.mod
が作成されているはずです。
// フォルダ構造 作業しているディレクトリ |---- go.mod
// go.modの中身
module example/hello
go 1.19
hello.goを作成&記述する
あとは〇〇.go
ファイルを作成して、コードを書いていきましょう。
私の場合はVisual Studio Code
を利用して進めていきます。
hello.go
を生成し、以下のコードを記述します。
// フォルダ構造 作業しているディレクトリ |---- go.mod |---- hello.go
// hello.go package main import "fmt" func main() { fmt.Println("Hello, World!") }
コードについてざっと解説すると以下の通り
- メインパッケージを宣言する(パッケージは同じディレクトリにある全てのファイルで構成される)
fmt
パッケージをインストールする(fmtパッケージ)- コンソールに
Hello, World!
を表示するmain
関数を実装する(main
パッケージを実行するとmain
関数がデフォルトで実行される)
コードを実行する
作業しているディレクトリで以下のコマンドを打てばコードが実行されます。
$ go run . Hello, World!
パッケージの利用方法を知る
fmt
パッケージについては言われた通りにインストールして利用したと思いますが、自力でパッケージを探して利用する手順を説明しておきます。
pkg.go.devページを開き利用したいパッケージを探す
まずはpkg.go.dev
を開くと、利用できるパッケージの一覧が表示されます。
pkg.go.dev
例えばquote
と検索すると、その内容に適したパッケージがずらっと表示されるはずです。
その中にあるquote (rsc.io/quote/v4)
というものを使いたくなったとします。
quote package - rsc.io/quote/v4 - Go Packages
コードに記述する
quote (rsc.io/quote/v4)
をインストールするようにコードを記述します。
// hello.go package main import ( "fmt" "rsc.io/quote/v4" ) func main() { fmt.Println(quote.Go()) }
ただこれを記述しただけではエラーが出力されていると思います。といいますのもgo.mod
に正しいモジュールの依存が記述されていないからですね。
コードから自動でgo.mod
を更新してくれるコマンドがあるので、それを実行します。
$ go mod tidy go: finding module for package rsc.io/quote/v4 go: downloading rsc.io/quote v1.5.2 go: downloading rsc.io/quote/v4 v4.0.1 go: found rsc.io/quote/v4 in rsc.io/quote/v4 v4.0.1 go: downloading rsc.io/sampler v1.3.0 go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go mod tidy ensures that the go.mod file matches the source code in the module. It adds any missing module requirements necessary to build the current module’s packages and dependencies, and it removes requirements on modules that don’t provide any relevant packages. It also adds any missing entries to go.sum and removes unnecessary entries.
// DeepL翻訳
go mod tidy は go.mod ファイルがモジュール内のソースコードと一致することを確認します。現在のモジュールのパッケージと依存関係をビルドするために必要なモジュールの要件を追加し、関連するパッケージを提供しないモジュールの要件を削除します。また、go.sum に不足しているエントリを追加し、不要なエントリを削除します。
Go Modules Reference - The Go Programming Language
無事に実行できると、go.mod
の中身が更新されるとと共にgo.sum
ファイルが生成されていることが確認できます。
// go.mod module example/hello go 1.19 require rsc.io/quote/v4 v4.0.1 require ( golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect rsc.io/sampler v1.3.0 // indirect )
ちなみにindirect
とは完成的に依存しているパッケージのことで、quote
パッケージがrsc.io/sampler
に依存して、sampler
パッケージがgolang.org/x/text
パッケージに依存して......といった具合です。
quote/go.mod at master · rsc/quote · GitHub
sampler/go.mod at master · rsc/sampler · GitHub
// go.sum golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= rsc.io/quote/v4 v4.0.1 h1:i/LHLEinr65wwTCqlP4OnMoMWeCgnFIZFvifdXNK+5M= rsc.io/quote/v4 v4.0.1/go.mod h1:w/DafQky66grMesu3uPhdDMS3knhBippwwemZtMOyCI= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
コードを実行する
最後はいつものコードを打ち、実行します。
$ go run . Don't communicate by sharing memory, share memory by communicating.
Go言語の基礎の基礎
関数
関数は0個以上の引数を取り、型名を後ろに書きます。
func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }
またx, y int
はx int, y int
の省略です。
複数の戻り値
func swap(x, y string) (string, string) { return y, x }
named return value
func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
戻り値となる変数に名前をつける(named return value
)ことができます。
戻り値に名前をつけると、関数の最初で定義した変数名として扱われます。
変数定義
func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" }
var
か:=
を使います。
:=
は暗黙的な型宣言です。
組み込み型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 の別名
rune // int32 の別名
// Unicode のコードポイントを表す
float32 float64
complex64 complex128
var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) )
Zero values(デフォルト値)
変数に初期値を与えずに宣言すると、ゼロ値( zero value )が与えられます。
ゼロ値は型によって以下のように与えられます:
数値型(int,floatなど): 0
bool型: false
string型: "" (空文字列( empty string ))
func main() { var i int var f float64 var b bool var s string }
型変換
var i int = 42 var f float64 = float64(i) var u uint = uint(f)
i := 42 f := float64(i) u := uint(f)
定数
func main() { const World = "世界" const Truth = true }
- 文字(character)
- 文字列(string)
- boolean
- 数値(numeric)
のみで利用可能。
for文
for i := 0; i < 10; i++ { sum += i }
while文
func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }
無限ループ
for {
}
if文
if x < 0 { return sqrt(-x) + "i" }
func pow(x, n, lim float64) float64 { // 評価するための簡単なステートメントを書ける if v := math.Pow(x, n); v < lim { return v } return lim }
if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) }
switch文
func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.\n", os) } }
Go では選択された case だけを実行してそれに続く全ての case は実行されません。 これらの言語の各 case の最後に必要な break ステートメントが Go では自動的に提供されます。
Defer
func main() { defer fmt.Println("world") fmt.Println("hello") }
defer ステートメントは、 defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させるものです。
...
defer へ渡した関数が複数ある場合、その呼び出しはスタック( stack )されます。 呼び出し元の関数がreturnするとき、 defer へ渡した関数は LIFO(last-in-first-out) の順番で実行されます。
ポインタ
func main() { i, j := 42, 2701 p := &i // point to i fmt.Println(*p) // read i through the pointer *p = 21 // set i through the pointer fmt.Println(i) // see the new value of i p = &j // point to j *p = *p / 37 // divide j through the pointer fmt.Println(j) // see the new value of j }
変数 T のポインタは、 *T 型で、ゼロ値は nil です。
...
なお、C言語とは異なり、ポインタ演算はありません。
構造体
type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 p := &v // (*p).Xの省略 p.X = 1e9 fmt.Println(v.X) }
var ( v1 = Vertex{1, 2} // has type Vertex v2 = Vertex{X: 1} // Y:0 is implicit v3 = Vertex{} // X:0 and Y:0 p = &Vertex{1, 2} // has type *Vertex )
Array
func main() { var a [2]string a[0] = "Hello" a[1] = "World" primes := [6]int{2, 3, 5, 7, 11, 13} }
Slice
func main() { primes := [6]int{2, 3, 5, 7, 11, 13} var s []int = primes[1:4] fmt.Println(s) }
スライスは配列への参照のようなものです。
スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示しています。
スライスの要素を変更すると、その元となる配列の対応する要素が変更されます。
// 以下は等価 a[0:10] a[:10] a[0:] a[:]
もっと厳密に書くとSlice
は以下を保持しています。
- 0番目の要素へのポインタ
- スライスの要素数(
len(slice)
) - スライスの容量(
cap(slice)
)
要素数と容量はlen
とcap
を用いることで調べられます。
func main() { s := []int{2, 3, 5, 7, 11, 13} printSlice(s) // 6 // Slice the slice to give it zero length. s = s[:0] printSlice(s) // 0 // Extend its length. s = s[:4] printSlice(s) // 4 // Drop its first two values. s = s[2:] printSlice(s) // 2 }
スライスのゼロ値(デフォルト値)はnil
が格納されています。
func main() { var s []int printSlice(s) // len=0 cap=0 [] // append works on nil slices. s = append(s, 0) printSlice(s) // len=1 cap=1 [0] // The slice grows as needed. s = append(s, 1) printSlice(s) // len=2 cap=2 [0 1] // We can add more than one element at a time. s = append(s, 2, 3, 4) printSlice(s) // len=5 cap=6 [0 1 2 3 4] }
Range
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } }
1つ目の変数はインデックス( index )で、2つ目はインデックスの場所の要素のコピー。
func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) // == 2**i } for _, value := range pow { fmt.Printf("%d\n", value) } }
簡単なREST APIを作成する
Gin
というフレームワークを利用すれば簡単にREST API
を作成できるようなのでやってみます。
gin-gonic.com
ginのインストール
以下のコマンドを実行してgin
をインストールします。
$ go get -u github.com/gin-gonic/gin
コードの記述
// example.go package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // 0.0.0.0:8080 でサーバーを立てます。 }
あとはコマンドを打ってサーバーを起動し、ブラウザで0.0.0.0:8080/ping
にアクセスしてください。
$ go run example.go
エディタについて
Visual Studio Code
を最初は使っていましたが、Jet Brains
社がGoLand
というエディタを出していることを発見しました。
Jet Brains
のRider
というエディタを私は愛用していたので、途中からGoLand
を利用し始めたところ最高でした、
わざわざ自分でコマンドを打ってパッケージをインストールせずとも、エラーを修正とかで自動でやってくれたりと至れり尽くせりです。
気になる方は是非調べてみてください。
www.jetbrains.com