Golang入門記事の没ネタサンプルコード

Golang入門中です。

以前、以下の記事を書きました。
[Golang入門：AtCoderなどの競技プログラミング問題を解き始めるための基本構文+その他おまけTips](https://qiita.com/shiro01/items/4e12a48816ecd47c0c0e)

記事に載せようかと思い書いてみたけれど、
趣旨から外れるため除外したサンプルコードをせっかくなのでまとめて公開したいと思います。

入門者によるざっくり試してみたコードの羅列ですが、参考になれば幸いです。

#型宣言

基本の型から独自の型を宣言できます。

```golang:newType01.go
package main

import (
	"fmt"
)

// intから新しい型を宣言
type aInt int
type bInt int

func main() {
	var a aInt = 5
	var b bInt = 55
	hoge(a, b)

	// 数値はaInt型,bInt型に代入できるので実行可能
	hoge(5, 55)

	// intだとエラーになる
	// エラー例:
	// # command-line-arguments
	// .\newType01.go:19:6: cannot use i (type int) as type aInt in argument to hoge
	// .\newType01.go:19:6: cannot use j (type int) as type bInt in argument to hoge
	// var i int = 5
	// var j int = 55
	// hoge(i, j)

	// 別の型として扱われるので、引数の順番を逆にするとエラーになる
	// エラー例:
	// # command-line-arguments
	// .\newType01.go:16:6: cannot use b (type bInt) as type aInt in argument to hoge
	// .\newType01.go:16:6: cannot use a (type aInt) as type bInt in argument to hoge
	// hoge(b, a)

	// 型に関連付けられたメソッドを実行
	a.show()

}

func hoge(x aInt, y bInt) {
	fmt.Printf("x :%T, %v\n", x, x)
	fmt.Printf("y :%T, %v\n", y, y)
}

// 新しい型に関数を関連付けられる
// 型の変数をレシーバ、関数をメソッドという
func (a aInt) show() {
	fmt.Println(a)
}
```

```golang:実行結果
$ go run newType01.go
x :main.aInt, 5
y :main.bInt, 55
x :main.aInt, 5
y :main.bInt, 55
5
```

#構造体
型を組み合わせて構造体が作れます。

```golang:typeStruct01.go
package main

import (
	"fmt"
)

type vector struct {
	x, y int
}

func main() {
	var v1 vector
	v1.x = 5
	v1.y = 55
	fmt.Printf("v1: %T, %v\n", v1, v1)

	v2 := vector{1, 2}
	fmt.Printf("v2: %T, %v\n", v2, v2)

	fmt.Printf("subXY(): %v\n", v1.subXY())
	fmt.Printf("addXY(): %v\n", v2.addXY())
}

// 型宣言と同様にメソッドを定義できる
func (v vector) addXY() int {
	return v.x + v.y
}

func (v vector) subXY() int {
	return v.y - v.x
}
```

```golang:実行結果
$ go run typeStruct01.go
v1: main.vector, {5 55}
v2: main.vector, {1 2}
subXY(): 50
addXY(): 3
```

#遅延実行
`defer`を使うと関数内で、最後に実行される処理を宣言できます。

```golang:defer01.go
package main

import "fmt"

func main() {
	// 関数の最後に実行される
	// 複数宣言した場合、後に宣言したdeferが先に実行される
	defer fmt.Println("a defer")
	defer fmt.Println("b defer")
	defer hoge()
	defer fmt.Println("c defer")
	fmt.Println("main")
}

func hoge() {
	defer fmt.Println("hoge defer")
	fmt.Println("hoge")
}
```

```golang:実行結果
$ go run defer01.go
main
c defer
hoge
hoge defer
b defer
a defer
```

#例外処理
Golangには例外処理の構文(try ~ catchなど)がありません。
替わりに関数から返されるerrorインターフェースをハンドリングします。

```golang:error01.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	e()
}

func e() {
	n, err := strconv.Atoi("aaa")
	// 関数が返す値が判定して例外処理する
	if err != nil {
		fmt.Printf("type: %T\nerr: %v\n", err, err)
		return
	}
	fmt.Println(n)
}
```

```golang:実行結果
$ go run error01.go
type: *strconv.NumError
err: strconv.Atoi: parsing "aaa": invalid syntax
```

typeはNumErrorですが、NumErrorにはerrorが含まれているっぽいです。

```golang:strconv.atoi.go
// 抜粋
type NumError struct {
	Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
	Num  string // the input
	Err  error  // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
}
```

#ポインタ
Golangではポインタがあります。

```golang:pointer01.go
package main

import (
	"fmt"
)

func main() {
	// &でアドレスを取得
	var n int = 10
	fmt.Println("nのアドレス: ", &n)
	fmt.Println("nの値: ", n)
	fmt.Println()

	//int型のポインタ変数宣言(アドレスを格納できる)
	var pointer *int

	// nのアドレスを代入
	pointer = &n
	fmt.Println("pointerの値: ", pointer)
	// アドレスから値取得
	fmt.Println("pointerの中身: ", *pointer)
	fmt.Println()

	a, b := 1, 1
	addOneByValue(a)      // 値渡しで加算
	addOneByPointer(&b)   // 参照渡しで加算
	fmt.Println("a: ", a) // 値渡しなので元の変数に影響なし
	fmt.Println("b: ", b) // 参照渡しなので変更される
	fmt.Println()

	s := []int{1, 2, 3}
	fmt.Println("sのアドレス: ", &s) // スライスは&でアドレス確認できない
	fmt.Printf("sのアドレス: %p\n", s)
	slicePointer(s) //スライスは参照渡し

}

// 値渡しの関数
func addOneByValue(a int) {
	a = a + 1
}

// ポインタ渡し(参照渡し)の関数
func addOneByPointer(a *int) {
	*a = *a + 1
}

// スライスを引数にした場合の渡し方の確認
func slicePointer(s []int) {
	fmt.Printf("sのアドレス: %p\n", s)
}
```

```:実行結果
$ go run pointer01.go
nのアドレス:  0xc0000140a0
nの値:  10

pointerの値:  0xc0000140a0
pointerの中身:  10

a:  1
b:  2

sのアドレス:  &[1 2 3]
sのアドレス: 0xc000016180
sのアドレス: 0xc000016180
```

#インターフェース
インターフェースを使用することで、ポリモーフィズムを実装することができます。

```golang:interface01.go
package main

import (
	"fmt"
)

type person interface {
	// say()を実装するとインターフェースを実装したことになる
	say()
}

func personSay(p person) {
	p.say()
}

type p1 struct{}

func (a p1) say() {
	fmt.Println("aaaaa")
}

type p2 struct{}

func (b p2) say() {
	fmt.Println("bbbbb")
}

type p3 struct{}

func (c p3) nosay() {
	fmt.Println(". . .")
}

// インターフェースを使うと、どの型も引数に取るれる、返せる関数が作れる
func personShout(someone interface{}) interface{} {
	_, ok := someone.(person)
	if !ok {
		fmt.Print("あああああ")
		return "aaaaa"
	} else {
		fmt.Print("ゴゴゴゴゴゴ")
		return 55555555
	}
}

func main() {
	a := new(p1)
	b := new(p2)
	// interfaceに関連づいたメソッド経由で実行
	personSay(a)
	personSay(b)

	c := new(p3)
	// personSay(c) // sayを実装していないのでエラーになる
	// エラー例
	// # command-line-arguments
	// .\interface01.go:41:11: cannot use c (type *p3) as type person in argument to personSay:
	//         *p3 does not implement person (missing say method)
	c.nosay() // メソッドとしては実行できる

	//
	fmt.Println(personShout(a))
	fmt.Println(personShout(c))
}
```

```golang:実行結果
$ go run interface01.go
aaaaa
bbbbb
. . .
ゴゴゴゴゴゴ55555555
あああああaaaaa
```


#並行処理
Golangと言えばで必ず上がるであろうGolangの強みです。
比較的簡単に並行処理の記述が可能になっています。
(そもそも並行処理が難しいですが。。)

##goroutine
`go <関数>`するとgoroutineと呼ばれる軽量なスレッドで<関数>が起動します。

```golang:goroutine01.go
package main

import (
	"fmt"
	"time"
)

func main() {
	// 呼び出し側の処理時間内に終わるため最後まで実行される
	go subfunc01()
	// 10~20まで出力する関数だが、呼び出し側の処理が終了すると終了する
	go subfunc02()
	for i := 100; i <= 103; i++ {
		fmt.Println(i)
		time.Sleep(1 * time.Second)
	}
	fmt.Println("end main")
}

func subfunc01() {
	for i := 0; i < 3; i++ {
		fmt.Println(i)
		time.Sleep(1 * time.Second)
	}
	fmt.Println("end subfunc01")
}

func subfunc02() {
	for i := 10; i < 20; i++ {
		fmt.Println(i)
		time.Sleep(1 * time.Second)
	}
	fmt.Println("end subfunc02")
}
```

```:実行結果
$ go run goroutine01.go
100
0
10
1
11
101
12
102
2
103
end subfunc01
13
14
end main
```

##Channel
golangではChannelを使用して、各goroutine間でメッセージの送受信ができます。
サンプルを書いてる最中に、「これ範囲外だな」と思い書ききらなかったのでサンプルがありません。。(´・ω・｀)

#外部パッケージの利用
他の言語同様にGolangでも公開されているパッケージの利用ができます。
外部パッケージを使用する場合は`go get <パッケージ>`で取得します。

ここではmysqlのドライバをgetしてDBにアクセスしてみます。
使用する外部パッケージ(mysql用のドライバ)：https://github.com/go-sql-driver/mysql

※以降はIPアドレス：192.168.10.10が割り当てられた環境で実施しています。

```bash:mysqlのドライバ取得
$ go get -u github.com/go-sql-driver/mysql
```

```bash:フォルダ構成
# GOPATHは「go env GOPATH」で確認
<GOPATH>/src/dbsample
　└ db.go
```

```sql:想定するテーブル
create table test_db.goods
(
  goods_id bigint not null auto_increment,
  name varchar( 255 ) not null,
  category varchar( 255 ),
  memo varchar( 255 ),
  index (goods_id),
  primary key (goods_id)
);

insert into test_db.goods (name, category) values ("PC0001", "PC");
insert into test_db.goods (name, category) values ("PC0002", "PC");
insert into test_db.goods (name, category) values ("MB0001", "携帯電話");
insert into test_db.goods (name, category) values ("MB0002", "携帯電話");
```

```golang:db.go
package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type Goods struct {
	goodsId  int    `db:goods_id`
	name     string `db:name`
	category string `db:category`
	memo     string `db:memo`
}

func main() {
	whereCategory := "PC"
	goodsRows := selectQuery(whereCategory)
	// *sql.Rows型からGoodsのスライスに詰め替えている。
	var goodsSlice []Goods
	for goodsRows.Next() {
		var goods Goods
		goodsRows.Scan(&goods.goodsId, &goods.name, &goods.category, &goods.memo)
		goodsSlice = append(goodsSlice, goods)
	}
	fmt.Println(goodsSlice)
}

func selectQuery(whereCategory string) *sql.Rows {
	// DBへの操作は標準パッケージのdatabase/sqlを使用。
	// DB接続(ホスト：192.168.10.10、DB：test_dbの場合)
	db, err := sql.Open("mysql", "master:master@tcp(192.168.10.10:3306)/test_db?charset=utf8mb4")
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	// ステートメント作成
	query := "SELECT * FROM goods;"
	stmt, err := db.Prepare(query)
	if err != nil {
		panic(err.Error())
	}
	defer stmt.Close()

	// クエリの実行
	rows, err := stmt.Query()
	if err != nil {
		panic(err.Error())
	}
	return rows
}
```

```bash:実行結果
$ go run db.go
[{1 PC0001 PC } {2 PC0002 PC }]
```

#標準パッケージのみでWebサーバー作成
標準パッケージで簡単なWebサーバーを作成してみます。
テンプレート機能もあります。簡単にですがコード例を紹介します。

※以降はIPアドレス：192.168.10.10が割り当てられた環境で実施しています。

```bash:フォルダ構成
sampleweb
　└ helloserver.go
　└ tmpl01.html
```

```golang:helloserver.go
package main

import (
	"fmt"
	"html/template"
	"net/http"
)

func main() {
	fmt.Println("Start Sample Server")
	server := http.Server{
		Addr: ":8080",
	}
	http.HandleFunc("/", index)
	server.ListenAndServe()
}

func index(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("tmpl01.html")
	if err != nil {
		fmt.Println("template parse error.", err)
	}

	helloworldSlice := []string{"hello", "world"}

	err = t.Execute(w, helloworldSlice)
	if err != nil {
		fmt.Println("template execute error.", err)
	}
}
```

```html:tmpl01.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sample</title>
</head>
<body>
  <body>
    <ul>
    {{ range . }}
      <li>{{ . }}</li>
    {{ else }}
      データなし
    {{ end}}
    </ul>
  </body>
</html>
```

```bash:実行結果
$ go run helloserver.go
Start Sample Server

(停止する場合はCtrl + C)
```

別コンソールからcurlを叩いてみます。

```html:実行結果
$ curl 192.168.10.10:8080
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sample</title>
</head>
<body>
  <body>
    <ul>

      <li>hello</li>

      <li>world</li>

    </ul>
  </body>
</html>
```

ブラウザでアクセスすると以下のように表示される。

![2020-01-10_10h06_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200585/dad07355-3e6e-b868-835d-384beddf87bc.png)


#おわり
Golangはシンプルで構文も少ない方だと思いますが、まだまだ勉強することが多く奥が深いです。
着実にできることを増やしていきたいです。

今回は以上です。

#参考
[The Go programming Language](https://golang.org/)
[Go言語.com](https://xn--go-hh0g6u.com/)
[The Go Blog 日本語訳](https://www.ymotongpoo.com/works/goblog-ja/)
[プログラミング経験者がGo言語を本格的に勉強する前に読むための本](https://www.amazon.co.jp/dp/B06XJ86BFZ)
[はじめてのGo言語](http://cuto.unirita.co.jp/gostudy/)
[Go の並行処理](http://jxck.hatenablog.com/entry/20130414/1365960707)