#author("2020-01-03T12:57:23+00:00","","") #mynavi() #setlinebreak(on) * 目次 [#j4af1b33] #contents - 参照 -- https://golang.org/doc/ (公式ドキュメント) -- https://github.com/golang/go - 関連 -- [[Goルーチン]] -- [[AWS SDK for Go を使ってみる]] // -- [[Goルーチンで並列HTTPリクエストを発行]] * Go言語とは [#g7f68402] #html(<div class="pl10">) Go言語はGoogleによって開発されているプログラミング言語で Linux、Mac、Window 等の主要OS 及び Android、iOS上でも動作する。(2016年現在) ※ Google App Engine でもサポートされている。 #html(</div>) * インストール [#n5409608] #html(<div class="pl10">) https://golang.org/doc/install#install ** Mac の場合 [#s0ed9876] #html(<div class="pl10">) Homebrew でのインストールが可能 #myterm2(){{ brew install go }} ※パッケージを落としてインストールする場合は https://golang.org/dl/ から取得 #html(</div>) インストール時に言われた通りにPATHを通す。 $HOME/.bash_profile #myterm2(){{ export GOPATH=/usr/local/opt/go/libexec export PATH=$PATH:$GOPATH/bin }} #html(</div>) * Hello World [#f5d79ee5] #html(<div class="pl10">) hello.go #mycode2(){{ package main import "fmt" func main() { fmt.Printf("Hello, World\n") } }} 実行 #myterm2(){{ go run hello.go }} #html(</div>) * 環境変数 [#l125530b] #html(<div class="pl10">) | 環境変数 | 説明 |h | GOROOT | 開発ツールのインストールPATH | | GOPATH | ワークディレクトリのPATH | | GOOS | ビルドした実行ファイルを実行させるOS | | GOARCH | ビルドした実行ファイルが実行可能なアーキテクチャ | #html(</div>) * 基本的な構文など [#jed80257] #html(<div class="pl10">) ** パッケージ [#c9143e5f] #html(<div class="pl10">) - 自身のパッケージは package で宣言する。 - 利用するパッケージは import で宣言する。 - main以外のパッケージと格納ディレクイトリは同じ名前にする。(慣例) #mycode2(){{ package main import "fmt" func main(){ fmt.Println("Hello World") } }} 複数 import は以下のように記述する事も可能 #mycode2(){{ : import ( "fmt" "encoding/json" ) : }} #html(</div>) ** 変数 [#t05f93f0] #html(<div class="pl10">) - 変数は var で宣言する。 - 初期値を指定した場合は型指定は省略できる。 - ローカルスコープの変数に限り、「変数名:= 式」で宣言する事も可能。(型宣言は指定不可) #mycode2(){{ // aを宣言 var a int // 初期値を指定してbを宣言 var b int = 1 // 初期値を指定してcを宣言(型指定を省略) var c = 0 // 複数の変数を宣言 var d1, d2 int var e1, e2 int = 1, 2 var f1, f2 = 1, 2 // ローカル変数の宣言例 func main(){ // aを宣言 var a int a = 1 // bの宣言と初期化(型指定は不可) b := 1 } }} #html(</div>) ** 定数 [#s07e7f3c] #html(<div class="pl10">) 定数は const キーワードで宣言する。 #mycode2(){{ package main import "fmt" const c1 string = "const1" func main(){ const c2 string = "const2" fmt.Printf("c1: %s\n", c1) fmt.Printf("c2: %s\n", c2) } }} #html(</div>) ** データ型 [#m516f8ea] #html(<div class="pl10">) *** 数値型(整数型) [#ee15c254] #html(<div class="pl10">) | データ型 | 説明 |h | int | 符号付き整数(4または8ビット) | | int8 | 符号付き整数(8ビット) | | int16 | 符号付き整数(16ビット) | | int32 | 符号付き整数(32ビット) | | int64 | 符号付き整数(64ビット) | | uint | 符号なし整数(4 または 8ビット) | | uint8 | 符号なし整数(8ビット) | | uint16 | 符号なし整数(16ビット) | | uint32 | 符号なし整数(32ビット) | | uint64 | 符号なし整数(64ビット) | | byte | 符号なし整数(8ビット) ※unit8と同じ | | rune | 符号付き整数(32ビット) ※int32と同じ | | uintptr | ポインタの値を符号なしの整数で格納 | #mycode2(){{ package main import "fmt" func main(){ var myInt int = 1 var myInt8 int8 = 1 var myInt64 int64 = 1 var myUint uint = 1 var myByte byte = 0x01 var myRune rune = 0x01 fmt.Printf("myInt : %T\n", myInt) fmt.Printf("myInt8 : %T\n", myInt8) fmt.Printf("myInt64 : %T\n", myInt64) fmt.Printf("myUint : %T\n", myUint) fmt.Printf("myByte : %T\n", myByte) fmt.Printf("myRune : %T\n", myRune) } }} 結果 #myterm2(){{ myInt : int myInt8 : int8 myInt64 : int64 myUint : uint myByte : uint8 myRune : int32 }} #html(</div>) *** 数値型(浮動小数点型) [#n2a3f143] #html(<div class="pl10">) | データ型 | 説明 |h | float32 | 32ビットの浮動小数点 | | float64 | 64ビットの浮動小数点 | &br; #mycode2(){{ package main import "fmt" func main(){ var myFloat32 float32 = 1.234 var myFloat64 float64 = 1.234 fmt.Printf("myFloat32 : %T, %f\n", myFloat32, myFloat32) fmt.Printf("myFloat64 : %T, %f\n", myFloat64, myFloat64) } }} 結果 #myterm2(){{ myFloat32 : float32, 1.234000 myFloat64 : float64, 1.234000 }} #html(</div>) *** 数値型(複素数型) [#h8f10322] #html(<div class="pl10">) | データ型 | 説明 |h | complex64 | float32型の実数+虚数 | | complex128 | float64型の実数+虚数 | #html(</div>) *** 文字列 [#x2bb026d] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ var myString string = "ABC" fmt.Printf("myString : %T, %s\n", myString, myString) } }} 結果 #myterm2(){{ myString : string, ABC }} #html(</div>) *** 真偽値 [#ged0ac08] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ var myBool bool = true fmt.Printf("myBool : %T, %v\n", myBool, myBool) } }} 結果 #myterm2(){{ myBool : bool, true }} #html(</div>) *** 配列/スライス [#rf2d5d93] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ // 数値の配列 var array1 [2]int array1[0] = 1 array1[1] = 2 for i,val := range array1{ fmt.Printf("array1[%d] = %d\n", i, val) } // 宣言と同時に初期化 array2 := []int{1,2} for i,val := range array2{ fmt.Printf("array2[%d] = %d\n", i, val) } // 文字列の配列 array3 := []string{"A", "B"} for i,val := range array3{ fmt.Printf("array3[%d] = %s\n", i, val) } } }} 結果 #myterm2(){{ array1[0] = 1 array1[1] = 2 array2[0] = 1 array2[1] = 2 array3[0] = A array3[1] = B }} #html(</div>) *** マップ [#x383f665] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ // マップの宣言と値の代入 var map1 map[string]string = make(map[string]string) //var map1 map[string]string = map[string]string{} // 上記と同義 //map1 := map[string]string{} // 上記と同義 map1["key1"] = "A" map1["key2"] = "B" for key,val := range map1{ fmt.Printf("map1[%s] = %s\n", key, val) } // 宣言と同時に初期化 map2 := map[string]string{"key1": "C", "key2": "D"} for key,val := range map2{ fmt.Printf("map2[%s] = %s\n", key, val) } } }} 結果 #myterm2(){{ map1[key1] = A map1[key2] = B map2[key1] = C map2[key2] = D }} #html(</div>) *** ポインタ [#jd20d0d6] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ num := 10 str := "ABC" var p1 *int = &num // numのアドレスを取得 var p2 *string = &str // strのアドレスを取得 fmt.Printf("p1 address: %v\n", p1) fmt.Printf("p1 value: %v\n", *p1) fmt.Printf("p2 address: %v\n", p2) fmt.Printf("p2 value: %v\n", *p2) } }} 結果 #myterm2(){{ p1 address: 0xc000016078 p1 value: 10 p2 address: 0xc000010200 p2 value: ABC }} #html(</div>) *** 構造体 [#gf5299bf] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" type MyStruct struct { name string age int } func main(){ var s1 MyStruct s1.name = "Taro" s1.age = 20 fmt.Printf("%T, %+v\n", s1, s1) var s2 = MyStruct{name: "Jiro", age: 10} fmt.Printf("%T, %+v\n", s2, s2) } }} 結果 #myterm2(){{ main.MyStruct, {name:Taro age:20} main.MyStruct, {name:Jiro age:10} }} #html(</div>) *** nil [#id63a35c] #html(<div class="pl10">) go には null は存在しない。 代わりにポインタ等が初期化されているかどうかの判断には nil が使用できる。 ちなみに string や int 等の変数には nil は代入できない。 #mycode2(){{ package main import "fmt" func main(){ str := "ABCD" var p1 *string if p1 == nil { p1 = &str fmt.Println("p1 set!") } fmt.Printf("%T, %+v\n", p1, *p1) } }} 結果 #myterm2(){{ p1 set! *string, ABCD }} #html(</div>) #html(</div>) ** 型宣言 [#v147f939] #html(<div class="pl10">) type を使用する事で、型に別名を付けて新しい型を作成する事ができる。 #mycode2(){{ package main import "fmt" type MyInt int type MyStruct struct { Id MyInt Name string } func main(){ x := MyStruct{Id: 100, Name: "Taro"} fmt.Printf("Id: %d\n", x.Id) fmt.Printf("Name: %s\n", x.Name) } }} #html(</div>) ** 条件分岐 [#r1e59357] #html(<div class="pl10">) *** if による条件分岐 [#x7fff487] #html(<div class="pl10">) #mycode2(){{ package main import "fmt" func main(){ x := 3 if x == 1 { fmt.Println("x is 1") } else if x == 2 { fmt.Println("x is 2") } else { fmt.Println("others") } } }} #html(</div>) *** switch による条件分岐 [#ud1039bd] #html(<div class="pl10">) 他の言語と同様に switch も使用できる。 ただし Java等と違って break を書く必要がない点が異なる。 逆に、次の case に遷移させたい場合は、fallthrough を使用する。 #mycode2(){{ package main import "fmt" func main(){ x := 3 switch x { case 1: fmt.Println("x is 1") case 2: fmt.Println("x is 2") case 3: fmt.Println("x is 3") fallthrough default: // x = 3 の時だけここが実行される fmt.Println("others") } // 以下の書き方も可能 switch { case x == 1: fmt.Println("x is 1") case x == 2: fmt.Println("x is 2") case x == 3: fmt.Println("x is 3") } } }} #html(</div>) #html(</div>) ** 繰り返し [#i8ffcf5f] #html(<div class="pl10">) 繰り返しは全て for で行う。 while や foreach は存在しないが、同等の処理を for のみで行う事が可能となっている。 #mycode2(){{ package main import "fmt" func main(){ // 通常のforループ for i := 0; i < 5; i++ { fmt.Printf("i = %d\n", i) } // 疑似while x := 0 for x < 5 { fmt.Printf("x = %d\n", x) x++ } // 永久ループ y := 0 for { fmt.Printf("y = %d\n", y) y++ if y >= 5 { break } } // 配列要素分繰り返し(疑似foreach) array1 := []int{1,2,3,4,5} for i, val := range array1 { fmt.Printf("val[%d] = %d\n", i,val) } // マップの要素分繰り返し(疑似foreach) m := map[string]int{"k1": 1, "k2": 2, "k3": 3, "k4": 4, "k5": 5} for key, val := range m { fmt.Printf("val[%s] = %d\n", key,val) } } }} #html(</div>) ** 関数 [#i345716b] #html(<div class="pl10">) - 関数は func で宣言する。 - 他のパッケージに公開する関数は、頭の1文字目を大文字にする。 - 複数の戻り値を返却する事も可能。 - 可変長引数を受け取る事も可能。 - 関数の終了処理を defer で定義する事ができる #mycode2(){{ package main import "fmt" // 通常の関数 func myAdd(a int, b int) int{ result := a + b return result } // 他パッケージに公開する関数 func MyAdd(a int, b int) int { return myAdd(a, b) } // 戻り値が2つの関数 func multiResult() (int, int){ return 1, 10 } // 可変長引数を受け取る関数 func multiArgs(a string, args...string){ // 関数の終了処理を defer で定義する事ができる defer func() { fmt.Println("multiArgs end!") }() fmt.Printf("a = %s\n", a) for i, val := range args { fmt.Printf("args[%d] = %s\n", i, val) } } func main(){ result := myAdd(1, 2) fmt.Printf("myAdd(1,2) = %d\n", result) x1, x2 := multiResult() fmt.Printf("multiResult() = %d, %d\n", x1, x2) multiArgs("a", "b", "c", "d") } }} #html(</div>) **インターフェース [#p9191eb1] #html(<div class="pl10">) メソッドの集まりをインターフェースとして宣言する事ができる。 これを利用して宣言すべきメソッドを定義する事が可能。 #mycode2(){{ package main import "fmt" // インターフェースの宣言 type Person interface { greet() } // 構造体の宣言 type PersonAttr struct { name string } // インターフェースに応じたメソッドの宣言 func (attr PersonAttr) greet(){ fmt.Printf("Hello! My name is %s\n", attr.name) } func main(){ attr := PersonAttr{name: "Taro"} var p Person = attr p.greet() } }} 実行結果 #myterm2(){{ Hello! My name is Taro }} メソッドが実装されていない時はビルドエラーとなる。 #mycode2(){{ package main import "fmt" // インターフェースの宣言 type Person interface { greet() hello() // メソッドを追加 } // 構造体の宣言 type PersonAttr struct { name string } // インターフェースに応じたメソッドの宣言 func (attr PersonAttr) greet(){ fmt.Printf("Hello! My name is %s\n", attr.name) } func main(){ attr := PersonAttr{name: "Taro"} var p Person = attr p.greet() } }} 実行結果 #myterm2(){{ # command-line-arguments ./test_interface.go:22:9: cannot use attr (type PersonAttr) as type Person in assignment: PersonAttr does not implement Person (missing hello method) }} #html(</div>) ** 例外処理 [#z50427ec] #html(<div class="pl10">) *** panic発生時の挙動 [#hb04a817] #html(<div class="pl10">) panic 関数が実行されると panic呼び出し以降の処理を中断し、コールスタックを巻き戻す。 #mycode2(){{ package main import "fmt" func func1() int { panic("func1 error!\n"); return 10 } func main(){ fmt.Println("main start") res := func1() fmt.Printf("result = %+v\n", res) fmt.Println("main end") } }} 実行結果 #myterm2(){{ main start panic: func1 error! goroutine 1 [running]: main.func1(...) /path_to/test_panic.go:14 main.main() /path_to/test_panic.go:21 +0x96 exit status 2 }} #html(</div>) *** recover によるコールスタック巻き戻しの停止 [#l11d8686] #html(<div class="pl10">) recover 関数は panic関数の引数を返し、コールスタックの巻き戻しを停止する。 #mycode2(){{ package main import "fmt" func func1() int { defer func(){ err := recover() if err != nil { fmt.Printf("error!! ... %+v", err) } }() panic("func1 error!\n"); return 10 } func main(){ fmt.Println("main start") res := func1() fmt.Printf("result = %+v\n", res) fmt.Println("main end") } }} 実行結果 #myterm2(){{ main start error!! ... func1 error! result = 0 main end }} #html(</div>) *** ライブラリを利用する場合 [#t6f8f5f8] #html(<div class="pl10">) 大方のライブラリでは戻り値で error も返却される為、これをエラー判定に利用する。 #mycode2(){{ f, err := os.Open("filename.ext") if err != nil { // エラー処理 log.Fatal(err) } }} #html(</div>) *** 自前の関数の実装時 [#r42d6824] #html(<div class="pl10">) 自前のライブラリ/関数を実装する際も必要に応じて、戻り値として error を返すインターフェースを採用する。 #mycode2(){{ package main import "errors" import "fmt" func myDiv(a int, b int) (int, error) { if b == 0 { return 0, errors.New("divide by zero") } result := a / b return result, nil } func main(){ res, err := myDiv(10, 0) if err != nil { fmt.Printf("Error!! ... %+v\n", err) } else { fmt.Printf("result = %v\n", res) } } }} 実行結果 #myterm2(){{ Error!! ... divide by zero }} #html(</div>) #html(</div>) ** Goルーチン [#nec2b113] #html(<div class="pl10">) [[Goルーチン]] を参照 #html(</div>) #html(</div>) * ビルド [#ke012467] #html(<div class="pl10">) ** ビルド時に指定する環境変数 [#cad1dd78] #html(<div class="pl10">) GOOS、GOARCH を使用してターゲットとなる OS、アーキテクチャを指定する事ができる。 #myterm2(){{ GOOS=linux GOARCH=amd64 go build -o main main.go }} GOOS 等を省略した場合は現在の環境用にビルドされる #myterm2(){{ go build -o main main.go }} #html(</div>) ** パッケージのビルド [#r371debc] #html(<div class="pl10">) ビルドしたパッケージで利用しているパッケージは実行ファイルに含まれる為、基本的に利用側のメイン処理をビルドするだけでOK?(TODO: 要調査) フォルダ作成 #myterm2{{ . ├── pkg ├── src │   ├── main.go │   ├── mypkg1 │   │   └── mypkg1.go │   │   └── other.go }} src/main.go #mycode2(){{ package main import "mypkg1" func main(){ mypkg1.PrintInfo() mypkg1.OtherModule() } }} src/mypkg1/mypkg1.go #mycode2(){{ package mypkg1 import "fmt" func PrintInfo() { fmt.Println("This is mypkg1.PrintInfo!") } }} src/mypkg1/other.go #mycode2(){{ package mypkg1 import "fmt" func OtherModule() { fmt.Println("This is mypkg1.OtherModule!") } }} ビルド #myterm2(){{ go build -o pkg/main src/main.go }} ビルドした処理を実行 #myterm2(){{ ./pkg/main This is mypkg1.PrintInfo! This is mypkg1.OtherModule! }} #html(</div>) #html(</div>) * ブラウザ上でGoのソースを実行する [#t2393a61] #html(<div class="pl10">) ブラウザ上で書いたソースを実行する環境が公式で提供されている。(無料) Go Playground https://play.golang.org A Tour of Go https://tour.golang.org #html(</div>)