Googleによって開発されたオープンソースのプログラミング言語
- シンプル
- コンパイル・実行速度が早い
- 充実した標準パッケージ
- 並行処理が容易
- ポータビリティ
- 強力なツール
- ダックタイピング
- イントロスペクション
- 型推論
- GC
- ʕ◔ϖ◔ʔ
- Google App Engine
- Docker, Packer, Consul/Serf, Terraform
- Vitess
- CloudFlare
ダウンロードページから環境にあったバイナリ/インストーラでインストール
クロスコンパイル環境を構築する場合はGo のクロスコンパイル環境構築 - Qiitaを参照
Goで開発では環境変数GOPATHの設定が必要
$ export GOPATH=$HOME
$ export PATH="$GOPATH/bin:$PATH"
$ go env GOPATH
...
-
GOPATH
- ワーキングディレクトリを設定する
- go getやgo installなど、goツールのベースパスとなる
- $GOPATH配下のディレクトリ構成
- bin コンパイル後に生成される実行ファイルの格納先
- pkg コンパイル後に生成されるパッケージの格納先
- src ソースコードの保存先でパッケージごとにサブディレクトリを作成
- importの参照先は$GOROOT/pkgまたは$GOPATH/pkgの、アーキテクチャ配下のパスとなる
$ go env GOROOT
-
GOROOT
- 標準ライブラリを探すためのベースパスが設定される
- JAVA_HOMEのようなもの
- 基本的には設定不要だが、Goインストール先を標準のパスから変更した場合は設定が必要
http://play.golang.org/p/AzJcxtdp5A
- セミコロン不要
- 実行にはmain()を含むmainパッケージが必要
- importで外部パッケージをインポート
http://play.golang.org/p/p6w6hUF36J
- 論理値型
- 数値型
- 文字列型
- 配列型
- スライス型
- 構造体型
- ポインタ型
- 関数型
- インタフェース型
- マップ型
- チャネル型
bool : 論理値(true, false)
uint8 : 符号なし 8ビット 整数
uint16 : 符号なし 16ビット 整数
uint32 : 符号なし 32ビット 整数
uint64 : 符号なし 64ビット 整数
int8 : 符号あり 8ビット 整数
int16 : 符号あり 16ビット 整数
int32 : 符号あり 32ビット 整数
int64 : 符号あり 64ビット 整数
float32 : IEEE-754 32-ビット 浮動小数値
float64 : IEEE-754 64-ビット 浮動小数値
complex64 : float32の実数部と虚数部を持つ複素数
complex128 : float64の実数部と虚数部を持つ複素数
byte : uint8の別名
rune : int32の別名
string : 文字列(値は不変)
uint : 32または64ビット
int : uintと同じサイズ
uintptr : ポインタの値を格納するのに充分な大きさの符号なし整数
http://play.golang.org/p/5EXfndS7Z2
- 条件の () は省略可能
- 三項演算子はない
http://play.golang.org/p/-8jQIbBH3c
- ループはforのみ
- ループの外に抜けるにはcontinue, break
- ネストされたループを抜けるにはラベルを使用する
- ++(--)は後置のみ。また式ではないため値へ評価されないので a := b++ などはNG
http://play.golang.org/p/aB3QEjr6f7
- break不要
- caseにはカンマ区切りのリストを指定可能
- caseには式も指定可能
http://play.golang.org/p/2qva2t_zcp
- funcで関数を宣言
- ...Tで可変個引数(引数は[]Tとなる)
- interface{}(空インタフェース型)で任意の型を受け取る
- スライスを可変個引数として渡す場合は、末尾に...をつける
- 関数は複数の値を返すことが可能
- 関数の返り値は全て無視かすべて受け取る。一部を無視したい場合は_(ブランク識別子)を使用する
- クロージャ
http://play.golang.org/p/3MoYcyhfFk
- (一般的には)戻り値としてのエラー値を検査することでエラー処理を行う
余裕があれば解説
http://play.golang.org/p/Dpz2S_eGT3
- deferでfinallyと似たようなことができ、関数の実行を遅延させることができる
- deferに指定された関数は、それが書かれている関数が終了する時点で実行される
http://play.golang.org/p/gPCTNNd11P
- panic(), recover()でtry-catch-finally的な実装が可能
- deferで呼ばれる関数内であればrecover()は機能するので、呼び出す関数にrecover()を記述することで例外処理をまとめることができる
- が、panic()は明らかに回復手段がない場合にだけ使用し、また、回復するのが絶対的に安全で無い限りrecover()は使用しない
http://play.golang.org/p/mM7iMigla-
- [...]で要素数を推論
- 配列は通常の値
- 別の配列への代入は要素のコピーとなる(ポインタではない)
http://play.golang.org/p/CTnQ6_0NZk
- スライスは配列内の連続した領域への参照(配列に対するビュー)
- スライスは配列へのポインタ(Data)、アクセスできる要素の数(Len)、最大のスライスの大きさ(Cap)をもつ
ref. research!rsc: Go Data Structures
http://play.golang.org/p/_1tqLU6E99
- スライスに要素を追加するにはappendを使用する
- スライスのCapが十分な大きさを持たない場合は、追加する値を含んだ新しい配列へのポインタをDataとするので注意が必要
- スライスにスライスをappendする場合は...を使用する
- スライスの縮小は、縮小したCapの新しいスライスを作り、既存のスライスから新しいスライスへ値をコピーする
http://play.golang.org/p/L9JWrwhBf1
- 配列やスライスのイテレーションにはrangeを使用する
http://play.golang.org/p/ivM5DvEyed
- 他言語でいうところの連想配列や辞書
- make()で作成する。宣言やnew()で作ると初期化されないので注意
- キーに使用できる型は等価演算子が定義されていなければならない。詳しくはこちらを参照
- マップのイテレートにもrangeを使用する
http://play.golang.org/p/LZQaLcVV6P
- 型レベルでのアクセス指定子はないが、下記のルールでパッケージレベルでアクセス制御される
- 大文字で始まるトップレベルの型、メソッド、変数名、構造体のフィールドはエクスポートされる(public)
- それ以外はエクスポートされない(private)
- 継承はないのでprotectedなフィールドはない
- フィールドにはタグを指定でき、reflectパッケージから参照可能
- 既存の型に名前付けをして別の型(具象型)を定義できる
- メソッドのレシーバは値型でもポインタ型でも定義できる
- レシーバが値型の場合は、値・ポインタ両方に対して呼び出し可能
- レシーバをポインタとする場合の指針
- レシーバのフィールドに対して変更を行う場合
- レシーバが巨大な場合
- メソッドの一貫性を持たせる場合
http://play.golang.org/p/5sUMUiABwm
- 型埋め込みによる暗黙の委譲(継承ではない)
- レシーバはフィールドであり、フィールドを含む構造体ではないので注意
- 同名のメソッドの場合は呼び出すフィールドを明示する
http://play.golang.org/p/9peN5uMc80
- インタフェースはメソッドの集合
- インタフェースでダックタイピングを実現
- 型埋め込み同様、インタフェースを埋め込むことが可能。インタフェースの継承と等価 3
- インタフェースが定義しているメソッドをすべて実装している限り、インタフェース型を持つ変数へどんな型でも代入可能
- 直接のフィールドへのアクセスが必要ない場合は、構造体自身ではなくパブリックのメソッドを定義したインタフェースだけを公開することで実装を隠蔽する 4
http://play.golang.org/p/xJkHdWz_0T
- キャストはCと同じような感じ。細かいルールはこちら
- 型アサーションは実行時にチェックされる
- 特定のインタフェースを実装した型なのか
- 期待した型なのか
- インタフェース型じゃない値はinterface{}にキャストしたあとで型アサーションを行う
- 型スイッチで複数の型に対する検査を行う
http://play.golang.org/p/y4Vzz1nybW
- ゴルーチンは軽量スレッドのようなもの(≠スレッド)
- go ステートメントで生成
http://play.golang.org/p/fMKP2_5S_v
- ゴルーチンの同期にはミューテックスや条件変数(syncパッケージ)を使用する方法もあるが、チャネルを使ったほうが自然
http://play.golang.org/p/3_Id04WVw1
- バックグランドでの処理実行の終了待ちにはsync.WaitGroupを使用すると便利
http://play.golang.org/p/UUEhlFRinu
- ゴルーチン間の通信を提供。双方向パイプに近い。メッセージ型を指定して利用する
- make()で作成する
http://play.golang.org/p/kWGHEZO1cF
- チャネルはバッファリング可能
http://play.golang.org/p/5Bzs1oo37T
- 複数チャネルを扱う場合はselectを使用する
runtime.GOMAXPROCS(runtime.NumCPU())
- 同時に利用可能なCPU数はデフォルトで1。並列度を上げる場合は環境変数GOMAXPROCSもしくはruntime.GOMAXPROCS()で設定する
- GOMAXPROCSを上げすぎてもスレッド間のコンテキストスイッチのオーバーヘッドで性能が下がる場合もあるので調整が必要
// user/user.go
package user
type User interface {
Name() string
}
func NewUser(name string) User {
if name != "" {
return &user{name}
}
return new(guest)
}
// user/types.go
package user
type user struct {
name string
}
func (u user) Name() string {
return u.name
}
type guest struct {}
func (_ guest) Name() string {
return "Guest"
}
- 大文字で始まるトップレベルの型、メソッド、変数名、構造体のフィールドはエクスポートされ、他のパッケージからアクセスできる(public)
- それ以外はエクスポートされず、同一パッケージからしかアクセス出来ない(private)
- パッケージはディレクトリ単位で構成される
- パッケージは複数ファイルに分割できる(上記user/user.goとuser/types.goは同じuserパッケージとなる)
$ go get github.com/codegangsta/martini
$ ls $GOPATH/pkg/darwin_amd64/github.com/codegangsta
inject.a martini.a
go get でサードパーティパッケージの取得とインストールを行う
$GOPATH
└── src
├── user
│ ├── types.go
│ └── user.go
└── userapp
└── main.go
$ go install user
go install でパッケージのビルド(go build)と$GOPATHへのインストールを行う
$GOPATH
├── pkg
│ └── darwin_amd64
│ └── user.a
└── src
├── user
│ ├── types.go
│ └── user.go
└── userapp
└── main.go
$ go install userapp
ビルド対象がmainパッケージの場合は実行可能なバイナリが生成される
$GOPATH
├── bin
│ └── userapp
├── pkg
│ └── darwin_amd64
│ └── user.a
└── src
├── user
│ ├── types.go
│ └── user.go
└── userapp
└── main.go
ファイル名や出力先を変えたい場合は go build -o <OUTPUT_PATH> を実行する
$ go build -o /tmp/fooapp userapp
$ ls /tmp
fooapp
$ GOOS=windows GOARCH=386 go install userapp
クロスコンパイルは環境変数GOOSとGOARCHを指定する(クロスコンパイル用の環境構築が必要)
$GOPATH
├── bin
│ ├── windows_386
│ │ └── userapp.exe
│ └── userapp
├── pkg
│ ├── darwin_amd64
│ │ └── user.a
│ └── windows_amd64
│ └── user.a
└── src
├── user
│ ├── types.go
│ └── user.go
└── userapp
└── main.go
go buildでも同様に環境変数を指定して実行する
$ GOOS=linux GOARCH=arm go build userapp
- go testとtestingパッケージを使用してテストを行う
- ファイル名は_test.goで終わるようにする
- Testで始まり(t *testing.T)のシグニチャをもつ関数を順番に実行する
テスト対象は "パッケージ" のuserパッケージ
// user_test.go
package user
import(
"testing"
"reflect"
"runtime"
)
func isType(t *testing.T, got interface{}, expected interface{}) {
gotT := reflect.TypeOf(got)
expectedT := reflect.TypeOf(expected)
if gotT != expectedT {
_, file, line, _ := runtime.Caller(1)
t.Errorf("\nLocation: %s:%d\nError: got %s, expected %s", file, line, gotT, expectedT)
}
}
func TestNewUser(t *testing.T) {
g := NewUser("")
isType(t, g, new(guest))
u := NewUser("hayajo")
isType(t, u, new(guest)) // fail
}
$ ls
types.go user.go user_test.go
$ go test
--- FAIL: TestNewUser (0.00 seconds)
user_test.go:14:
Location: /<PWD>/user_test.go:23
Error: got *user.user, expected *user.guest
FAIL
exit status 1
FAIL _/<PWD> 0.006s
-
デバッグにはgdb(>= 7.1)を使う
-
Goの最適化を無視するために -gcflags '-N -l' をつけてビルドする
$ go build -gcflags '-N -l'
-
gdbでruntime-gdb.pyをロードしてデバッグを行う(~/.gdbinitに書いてもOK)
(gdb) source <$GOROOT/pkg/runtime/runtime-gdb.py>
// myapp.go
package main
import "fmt"
func main() {
a := make([]int, 5)
fmt.Println(a[9]) // panic
}
$ go build -gcflags '-N -l' myapp.go
-gcflags '-N -l' をつけてビルドする
$ gdb myapp
デバッグ開始
(gdb) source <PATH_TO_runtime-gdb.py>
runtime-gdb.pyをロードする(必要な場合)
(gdb) break runtime.panicindex
runtime.panicindex にブレークポイントを設定
(gdb) run
...
Breakpoint 1, runtime.panicindex () at ...
プログラム実行
(gdb) up
#1 0xXXXXXXXXXXXXXXXX in main.main () at /.../myapp.go:7
7 fmt.Println(a[9])
upして呼び出し元に戻る
(gdb) info locals
a = []int = {0, 0, 0, 0, 0}
ローカル変数の確認
(gdb) p $len(a)
$1 = 5
(gdb) p $cap(a)
$2 = 5
lenとcapの確認
マップを追加しました