Golang 入門

個人的なメモとして用いる。

## GOの特徴
- コンパイラ言語
- 静的型付け言語
- 文法がシンプル
- 並行処理プログラミング
- クロスコンパイルが可能
- 標準パッケージが豊富

## ソースコードの構成
```main.go
// パッケージの定義
package main

// 標準パッケージや外部ライブラリのインポート
import "fmt"

// main関数の定義
func main {
  fmt.Println("Hello World")
}
```


## プログラムはどこから実行されるのか
- main関数
    - mainパッケージに存在する
    - ソースコードを追う時はここから
    - プログラムのエントリポイントで、初期化後はここから実行される
    - webサーバの場合は常に実行されているので代わりにhttpハンドラを読む

## GOPATH
- GOPATHとは？
    - Goのソースコードやビルドされたファイルが入るパスが設定されている
    - インポートされるパッケージもここから検索される
    - 環境変数として設定される
    - デフォルトが決まっている
        - Unix系：$HOME/go
        - Windows：%USERPROFILE%\go
    - 複数設定できる
    - `go env GOPATH` コマンドで取得可能

実際のGOPATH

- $GOPATH
    - bin
        - fuga `ビルドされた実行可能ファイルが入る`
    - pkg
        - darwin_amd64 `ビルドされたパッケージが入る`
            - hoge.a
    - src
        - github.com
            - Kanai-Yuki
                - sample `project`
                    - main.go `実行ファイル`
                - sample2 `project`
                    - main.go `実行ファイル`

## Goプログラムの構成要素
- 型
- 関数
- 変数
- 定数
- パッケージ

## 型
### 組み込み型（最初から使える）

|種類 |型  |ゼロ値  |
|---|---|---|
|整数  |int, int8, int16, int32, int64<br>uint, uint8, uint16, uint32, uint64<br>uintptr, byte, rune |0  |
|浮動小数点数  |float32, float64  |0  |
|複素数 |complex64, complex128 |0  |
|文字列 |string |""  |
|真偽値 |bool |false  |
|エラー |error |nil |

### 型のキャスト
下の`sample.go`はコンパイルエラーになる。
原因はif文で用いる変数と浮動小数点数の不一致。

```sample.go
var sum int
sum = 5 + 6 + 3
avg := sum / 3

// float64(avg)とキャストすればエラーではなくなる
if avg < 4.5 {
	fmt.Println(avg)
}
```

キャストできない場合には、エラーとなる
例）`int("hello world")` など

### コンポジット型
複数のデータ型が集まって1つの型となっている

|型の種類 |説明 |ゼロ値 |
| --- | --- | --- |
|構造体 |型の異なるデータ型を集めたデータ型 |全フィールドがゼロ値 |
|配列 |同じ型のデータを集めて並べたデータ型 |要素が全てゼロ値 |
|スライス |配列の一部を切り出したデータ型 |nil |
|マップ |キーと値をマッピングさせたデータ型 |nil |

### 構造体
- 型のデータ型の変数を集めたデータ構造
    - 各変数はフィールドと呼ばれる
    - フィールドの方は自由
    - フィールドの方にはコンポジット型やユーザ定義型も使用可能

```sample.go
type Person struct {
    Name string
    age  int
}

bob := Person{"Bob", 30}
fmt.Println(bob.Name, bob.Age) // Bob 30
```

また以下のように初期化関数を作成することで初期化をすることも一般的に行われます。

```sample.go
func newPerson(name string, age int) *Person {
     person := new(Person)
     person.Name = name
     person.Age = age
     return person
}

func main(){
  var jen *Person = newPerson("Jennifer", 40)
  fmt.Println(jen.Name, jen.Age) //=>Jennifer 40
}
```

### 配列
- 同じ型のデータを集めて並べたデータ構造
    - 要素の型は全て同じ
    - 要素数が違えば違う型
    - 要素数は変更できない
    - 型は型リテラルで記述することが多い

```sample.go
// 初期化
var n [5]int
var n [5]int{1, 2, 3, 4, 5}
n := [...]{1, 2, 3, 4} //lengthを値から推論
n := [...]{3: 20, 4: 11} //要素数: 5, n[3] = 20, n[4] = 11

// アクセス
n[1] // 添字は変数可
n[2:3] //スライス演算

```

### スライス
- 配列の一部を切り出したデータ構造
    - 要素の型は全て同じ
    - 要素数は型情報に含まない
    - 背後に配列が存在する

```sample.go
// 初期化 // アクセスは配列と同様
var n []int // 変数にゼロ値を代入
n := make([]int, 3, 10) // 長さと容量を指定して初期化, 各要素䛿ゼロ値で初期化される
var n = []int{1, 2, 3, 4, 5} // スライスリテラルで初期化, 要素数は指定しない, 自動で配列が作られる

n = append(n, 10, 30) // 要素の追加 // 容量が足りない場合は背後の配列が再確保される
len(n) // 長さ
cap(n) // 容量
```


### マップ
- キーと値をマッピングさせるデータ構造
    - キーと値の型を指定できる
    - キーには「==」で比較できる型しかNG

型リテラル
`var n map[string]int`

```sample.go
// 初期化
var n map[string]int
var m = make(map[string]int)
n := make(map[string]int, 10) // 容量の指定
m = map([string]int){"a": 2, "b": 4}

// 操作
n["a"] // アクセス
n["a"] = 20 // キーの値を上書き

n, ok := m["a"] // キー存在の確認, ok << true, false
if _, ok := m["no"]; !ok {
    fmt.Println("そのキーはありません")
}

delete(m, "z") // 要素の削除
```

```sample.go
// マップのバリューがスライス
var a map[string][]int
a = map[string][]int{
	"a": []int{1, 2, 3},
	"b": []int{4, 5, 6},
	"c": []int{7, 8, 9},
}
fmt.Println(a["a"][0])
```

### ユーザ定義型
- typeで名前をつけて新しい型を定義する `type 型名 基底型`

```sample.go
// 組み込み型を基にする
type MyInt int

// 他のパッケージを基にする
type MyWriter io.Writer

// 型リテラルを基にする
type Person struct {
    Name string
}
```

### 型リテラル
- 型リテラルとは
    - 型の具体的な定義を書き崩した型の表現方法
    - コンポジット型などを表現するために使う
    - 変数定義やユーザ定義型などで使用する

```sample.go
// intのスライスを用いた変数定義
var n []int

// mapの型リテラルを用いた変数定義
var m map[string]int
```

リテラル = 識別子（名前）が付与されていないもの

## 関数
- 一連の処理をまとめたもの
    - 引数で受け取った値を基に処理を行い戻り値として結果を返す機能
    - 引数：関数䛾入力となるものf(x)の場合x
    - 戻り値（返り値）: 関数の出力となるもの
- 戻り値がある場合䛿変数に代入したり式中で使う
    - `x := f(12, 1+1, y)`

### 組み込み関数

| 関数名 | 機能 |
| --- | --- |
| print, println | 表示を行う |
| make | コンポジット型の初期化 |
| new | 指定した型のメモリ |
| len/cap | スライスの長さ/容量を返す |
| copy | スライスのコピー |
| delete | マップから指定したキーのエントリ |
| complex | 複素数型を作成 |
| imag/real | 複素数の虚部/実数部を取得 |
| panic/revocer | パニックを起こす/回復する |

### 関数の定義

```sample.go
// 関数の定義方法
func add(x, y int) int {
}

// 複数の戻り値を返す
func swap(x int, y int) (int, int) {
    return x, y
}

// 名前付き戻り値
func swap(x y int) (x2, y2 int) {
    x2, y2 := x, y
    return
}

func main() {
    x, y := swap(1, 2) // 返り値はカンマで区切って取得する
}
```

#### クロージャ
- 無名関数とも呼ばれる
- 定義と実行のタイミングを気をつける
    - 関数外の変数を参照している場合
    - 実行のタイミングでは値が変わっている可能性がある

```sample.go
// 「引数に文字列を取り、戻り値に文字列を返す関数」を返す関数
func later() func(string) string {
	var store string

	// 前の関数の呼び出し時に格納されている変数の値を返す
	return func(next string) string {
		s := store
		store = next
		return s
	}
}

func main() {
	f := later()
	fmt.Println("a: " + f("Golang"))
	fmt.Println("b: " + f("is"))
	fmt.Println("c: " + f("awesome!"))
	fmt.Println("d: " + f(""))
}
```

#### メソッドとレシーバ
- Goではクラスの概念がないが、ユーザ定義型に対してメソッドを定義することができる
    - `type`で型を定義し、レシーバ・ユーザ定義型を指定することでメソッドを定義することができる
- レシーバにできる型
    - `type`で定義した型
        - ユーザ定義型をレシーバにできる
    - ポインタ型
        - レシーバに変更を与えたい的
    - 内部にポインタを持つ型
        - マップやスライスなどもレシーバにできる

```sample.go
func (<レシーバ引数>) <関数名>([引数]) [戻り値の型] {
    [関数の本体]
}
```

```sample.go
func (p Person) intro(greetings string) string{
    return greetings + " I am " + p.Name
}

type T int

func (t *T) M() {
    fmt.Println("method")
}

func main(){
    bob := Person{"Bob", 30}
    fmt.Println(bob.intro("Hello")) // Hello I am Bob

    var t T
    p := &t
    p.M() // method
}
```

## 変数
- メモリに名前をつけて値を格納する
- 変数は型を持つ
- 変数に違う型の値を代入することはできない
- 暗黙の型演算がない
- 型推論がある

### 変数定義
```sample.go
// 変数定義と代入を同時に
var n int = 100
// 変数代入時に型推論
var n = 100
// 変数に後から代入
var n int
n = 100
// varを省略, 型推論
n := 100
// まとめて定義
var (
    n = 100
    b = 1000
)
```

## 定数
- 値の変わらないもの
- リテラルで記述される場合が多い
- 型推論ではデフォルトの型を使用
- 桁区切りには`_`を用いる
    - 例）100_000_000

### 定数定義

```sample.go
const n int = 100
const n = 100
const n = "hello" + "world" // 定数式, コンパイル時に計算される

const (
    n = 100
    m = 200
)
```

## 制御構文
### 条件分岐: if
```sample.go
// 通常のif文
if x == 0 {
    fmt.Println("xは0です")
} else if x == 1 {
    fmt.Println("xは1です")
} else {
    fmt.Println("xは2以上です")
}

// 代入if文
if a := f(); a < 1 {
    fmt.Println(a)
} else {
    fmt.Println(a*2)
}
```

### 条件分岐: switch
```sample.go
switch a {
case 1, 2:
    fmt.Println("a is 1 or 2")
case a == 3:
    fmt.Println("a is 3")
default
    fmt.Println("default")
}
```

### 繰り返し: for
Golangの繰り返しはfor文のみ

```sample.go
// 初期化; 継続条件; 更新
for i := 0; i <= 10; i++ {
    fmt.Println(i)
}

// 継続のみ
for i <= 100 {
}

// rangeを使った繰り返し
for i, v := range []int{1, 2, 4, 8} {
    fmt.Println(i, v)
}
```

### ひたすら簡単なおみくじプログラム
```sample.go
package main

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

func main() {
	rand.Seed(time.Now().Unix())

	results := map[int]string{
		0: "凶",
		1: "小吉",
		2: "小吉",
		3: "吉",
		4: "吉",
		5: "中吉",
		6: "大吉",
	}

	for i := 0; i <= 10; i++ {
		num := rand.Intn(8)
		if a := results[num]; a == "" {
			fmt.Printf("サイコロに%vの目はありません\n", num)
		}
		fmt.Printf("あなたの今年の運命は%vです\n", results[num])
	}
}
```

## ポインタ
- ポインタ = `0xc000016068` 的なもの
- 変数の格納先を表す値
    - 値で渡される型の値に対して破壊的な操作を加える際に利用する
        - 破壊的な操作 = 関数を出てもその影響が残る

```sample.go
func f(xp *int) {           // intのポインタ型
    fmt.Println("xp", xp)   // xp 0xc000016068
    fmt.Println("*xp", *xp) // *xp 0
    fmt.Println("&xp", &xp) // &xp 0xc00000e028

    *xp = 100               // *でポインタの指す先に値を入れる
}

func main() {
    var x int      // ゼロ値 = 0
    f(&x)          // &でポインタを取得する
    fmt.Println(x) // x == 100
}
```

- 以下の型では内部でポインタが用いられているためポインタを使用する必要がない
    - スライス
    - マップ
    - チャネル

```sample.go
ns := []int{10, 20, 30}
ns2 := ns
ns[1] = 200
fmt.Println(ns[0], ns[1], ns[2])    // 10 200 30
fmt.Println(ns2[0], ns2[1], ns2[2]) // 10 200 30
```

## パッケージ
- 関数や定数・変数・型を意味のある単位でまとめたもの
- Goプログラムはパッケージの組み合わせでできあがる
    - mainパッケージから別のパッケージをimportする
        - importすることで様々な機能が使えるようになる

標準パッケージ：https://golang.org/pkg/

| package名 | 用途 |
| --- | --- |
| fmt | 書式に関する処理など |
| net/http | webサーバ |
| archive, comress | zipなどの圧縮形式 |
| encording | JSON, XML, CSV, TSVなどのファイル形式 |
| html/template | web用のHTMLを生成する機能 |
| os, path/filepath | osへのアクセスや操作、ファイル操作など |

### パッケージ外へのエクスポート
- 先頭を大文字にした識別子がエクスポートされる
    - `var Abc string // exported`
    - `var abc string // unexported`

### `go get` でライブラリの取得をする
- goのライブラリを取得するコマンド
- 依存するライブラリも一緒に取得してくれる
- 指定した場所からダウンロード＆インストールしてくれる

```
$ go get github.com/tenntenn/greeting
$ ls $GOPATH/src/github.com/tenntenn/greeting
README.md greeting.go
```

### `go mod`コマンド
- `go mod`コマンドを有効にする
    - 環境変数の`GO111MODULE`を`on`にする
- `go mod`コマンドのサブコマンド

| コマンド | 処理 |
| --- | --- |
| go mod init<br>go mod init モジュール名 | 指定したモジュール名でgo.modファイルを作成する<br>モジュール名を省略するとGOPATHから推測する |
| go mod tidy | 使用していないパッケージのgo.modからの削除<br>必要なパッケージのダウンロードとgo.modへの追加 |
| go mod why | 指定したパッケージがなぜ必要になったか表示 |
| go mod vendor | 依存するパッケージをvendor以下にコピーする |