Go言語入門メモ

# 背景
Go言語よくわからないので学習したメモを残す

# Go言語特徴・メリット

## 構文がシンプル
C言語を基本としながらも文末のセミコロン不要といった、ソースコード量が少ない
（Rubyもセミコロン不要）

## コンパイルが速い
- 構文がシンプルになった。
- includeファイルを採用していない。

といったことから、C言語系のコンパイラに比べ速い。

## 並列処理をサポート
複数の処理を同時にかつ効率的に実行可能。「ゴルーチン」という単位で管理されている。

## GCサポート

## 安全性が高い（バグによるメモリ破壊を回避）
- Go言語にはポインタがあるが、不正なメモリアクセスができないようにポイント演算ができない
- 型のチェックが厳密

## 継承がない
- クラスがない
- 変わりに「埋め込み」・「インタフェース」がある

## エラーハンドリングが容易
- try〜catchで囲うような事が不要
- 必要なところでエラーハンドリングの記述をすればよい

# 開発環境について

## コンパイラ
gc あるいは GCC

## プログラムファイル拡張子
.go

## インストール（MacOSX）
```
brew install go
```

## バージョン確認
```
% go version
go version go1.11.2 darwin/amd64
```

## コマンドによる直接実行
```
go run {プログラムファイル名}
```

## 実行ファイルを生成する
```
go build {プログラムファイル名}
```

## コメントアウトの方法
### 単一
```go
// 単一行コメントアウト
```

### 複数行
```go
/*複数行
コメントアウト
*/
```

# goコマンド確認

## go help
```
Go is a tool for managing Go source code.

Usage:

	go <command> [arguments]

The commands are:

	bug         start a bug report
	build       compile packages and dependencies
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources
	generate    generate Go files by processing source
	get         download and install packages and dependencies
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	run         compile and run Go program
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

	buildmode   build modes
	c           calling between Go and C
	cache       build and test caching
	environment environment variables
	filetype    file types
	go.mod      the go.mod file
	gopath      GOPATH environment variable
	gopath-get  legacy GOPATH go get
	goproxy     module proxy protocol
	importpath  import path syntax
	modules     modules, module versions, and more
	module-get  module-aware go get
	packages    package lists and patterns
	testflag    testing flags
	testfunc    testing functions

Use "go help <topic>" for more information about that topic.
```

## go bug
bugレポート機能
以下コマンドを実行すると、Go言語に対するGithubのissue発行画面が表示されます。

```
go bug
```


## go clean

buildした結果の実行ファイル等がclearされる。
以下実行イメージ

```sh
% ls
hello.go hello

% go build hello.go
% ls
 hello    hello.go
% go clean hello.go
% ls
hello.go
%
```


## go doc
godoc対応用にコメントを記載しておくことで、コメントを参考にテキストが出力される
go doc 実行フォルダには、以下のコードを格納している。
https://qiita.com/ynarasak/items/10a903366e697b67a20c#helloworld%E3%81%A7%E8%80%83%E5%AF%9F


```
% go doc
これはGo言語テスト用プログラム

    package main
    runしたい場合にはmainパッケージを定義して func mainを定義するのが必須
```


## go env
Go内の環境変数確認ができます

```
% go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/xxxxx/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/xxxxx/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11.2/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11.2/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/kl/_97x1fqx5_3_j1wrylh6x2cm0000gp/T/go-build664765921=/tmp/go-build -gno-record-gcc-switches -fno-common"
```

## go fix
指定されたパッケージについてインポートパスを軸に更新される
更新された新しいパッケージが正常に読み込まれない場合とかに使うっぽい

```sh
# fmtパッケージを指定した場合
go fix fmt
```

## go fmt
フォーマッター
空白インデントがタブに置き換わり、波括弧の前に空白が入ったり、packageとimportの間に改行入ったりと、適切に空白・改行・タブを入れてくれる

```
go fmt hello.go
```

####適用前
```go
package main
import "fmt"
func main(){
 // Println は JavaのSystem.out.printlnに酷似している。ダブルクォーテーションではなくシングルにするとエラー
 fmt.Println("Hello, World!") //test
}
```

####適用後
```go
package main

import "fmt"

//func -> function の意味となるが、functionって記載するとエラーになります
func main() {
    // Println は JavaのSystem.out.printlnに酷似している。ダブルクォーテーションではなくシングルにするとエラー
    fmt.Println("Hello, World!") //test
}
```

## go get
指定されたパッケージがGOPATHに展開され、ビルドもしてくれる。
PATHをきちんと通しておけばそのままパッケージとして利用できるようになります。


```sh
# この例は、stringer パッケージをgetしてきた例です
% go env | grep GOPATH
9:GOPATH="/Users/xxxxx/go"
% ls -trl /Users/xxxxx/go                                                                                                                                                                                                    ls: /Users/xxxxx/go: No such file or directory
% go get golang.org/x/tools/cmd/stringer
% ls -trl /Users/xxxxx/go
total 0
drwxr-xr-x  3 xxxxx  staff  102 Nov 14 15:05 src
drwxr-xr-x  3 xxxxx  staff  102 Nov 14 15:05 bin
%
# GOPATHを環境変数にセットする
% unset GOPATH | exp_path=`go env | egrep "GOPATH=" | sed -e 's/"//g'` | export $exp_path
# GOPATH/binをPATHへセットする
% export PATH=$PATH:$GOPATH/bin
# stringer が利用できる
% stringer
Usage of stringer:
	stringer [flags] -type T [directory]
	stringer [flags] -type T files... # Must be a single package
For more information, see:
	http://godoc.org/golang.org/x/tools/cmd/stringer
Flags:
  -linecomment
    	use line comment text as printed text when present
  -output string
    	output file name; default srcdir/<type>_string.go
  -tags string
    	comma-separated list of build tags to apply
  -trimprefix prefix
    	trim the prefix from the generated constant names
  -type string
    	comma-separated list of type names; must be set

```

## go generate
ソースコードの中に `//go:generate {実行したいコマンド}` と記載すると、実行したいコマンドが実行される

以下のようなコードを書いて

```go:generate.go
package generate

//go:generate echo "echo generate!!!!!"

func gen(){

}
```

実行すると以下のようになる

```sh
% go generate generate.go
echo generate!!!!!
%
```

これだと正直何が嬉しいのか？となるが、先ほど`go get`で取得した`stringer`を用いると
ありがたみがわかる。

stringerを記載したgoのコード

```go:./painkiller/painkiller.go
package painkiller

type Pill int
//go:generate stringer -type=Pill
const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)
```

go generate実行

```sh
go generate painkiller.go
```

以下のように、pill_string.so というファイルが生成される

```sh
% ls
painkiller.go  pill_string.go
```

自動生成されたコードの内容

```go:./painkiller/pill_string.go

// Code generated by "stringer -type=Pill"; DO NOT EDIT.

package painkiller

import "strconv"

const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"

var _Pill_index = [...]uint8{0, 7, 14, 23, 34}

func (i Pill) String() string {
	if i < 0 || i >= Pill(len(_Pill_index)-1) {
		return "Pill(" + strconv.FormatInt(int64(i), 10) + ")"
	}
	return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}
```

## go install

GOPATHに格納されているmainが格納されているパッケージ名（ディレクトリ名）をパラメータに指定すると
GOPATH/binにbuildした実行ファイルがinstallされる。

自作のhello.goをGOPATHに格納してgo installしてみた結果

```sh
% go clean hello.go
% mkdir $GOPATH/src/hello
% mv hello.go $GOPATH/src/hello
% go install hello
% ls $GOPATH/bin/
hello    stringer
% hello
Hello, World!
```

## go vet
コードの静的解析を行う。mainのようなファイルではなく個別パッケージをチェックする際に
効果的にコードチェックをするための用途として利用できそう。

例：`hello.go`の`import "fmt"`を`import fmt`に変更して実行

```sh
% go vet hello.go
can't load package: package main:
hello.go:9:11: expected 'STRING', found newline
hello.go:12:1: expected ';', found 'func'
```

## go test
以下のようなコードを準備して

```go:example.go
package example
func main(){
}

func Example(param string) int {
  if param == "success" {
    return 0
  }
  return 99
}
```

```go:example_test.go
package example
import "testing"
func TestExampleSuccess(t *testing.T){
  result := Example("success")
  if result != 0 {
    t.Fatal("failed test")
  }
}
```

以下の様なコマンドを実行するとテストされる

```sh
% go test
PASS
ok  	_/Users/xxxxx/Projects/study/go_test/example	0.008s
```


## その他
`go list`, `go mod`は利用用途がいまいちわかっておらず。宿題


# HelloWorldで考察

```go:hello.go
/*
 これはGo言語テスト用プログラム
  package main
  runしたい場合にはmainパッケージを定義して func mainを定義するのが必須
*/
package main
//import fmt -> fmtという名前のpackageを読み込んでいる。ダブルクォーテーション必須
import "fmt"

//func -> function の意味となるが、functionって記載するとエラーになります
func main() {
  // Println は JavaのSystem.out.printlnに酷似している。ダブルクォーテーションではなくシングルにするとエラー
  fmt.Println("Hello, World!")
}
```

## package mainでない場合
```
go run: cannot run non-main package
```

## import "fmt"が存在しない場合
```
undefined: fmt
```

## `import "fmt"` を誤って `import fmt` と記載してしまった場合
```
package main:
expected 'STRING', found newline
expected ';', found 'func'
```

## func mainが存在しない場合
```
runtime.main_main·f: function main is undeclared in the main package
```

## `func main(){` の `{`前で改行してしまった場合
以下の様にすると

```go
func main()
{
  fmt.println("xxxx")
}
```
以下の様なエラーになる

```
missing function body
syntax error: unexpected semicolon or newline before {
```

**GO言語では波括弧の位置が決められており、波括弧の位置で不毛な論争が起こることはないそうw**



## `"Hello, World"` ではなく `'Hello, World'` のようにシングルクォートで囲った場合
```
invalid character literal (more than one character)
```




## 参考
https://www.amazon.co.jp/dp/4863541171