- Golang Cheat Sheet
- No classes, but structs with methods
- Interfaces
- No implementation inheritance. There's type embedding, though.
- Functions are first class citizens
- Functions can return multiple values
- Has closures
- Pointers, but not pointer arithmetic
- Built-in concurrency primitives: Goroutines and Channels
Linux
# download go
https://github.com/golang/go/releases
### STANDARD WAY
sudo tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
# set PATH
# Add /usr/local/go/bin to PATH with /etc/profile or $HOME/.profile
export GO_HOME=/usr/local/go
export PATH=$PATH:$GO_HOME/bin
MacOS
# (1) install go by brew
brew install go
# or
brew upgrade go
# (2) Env variables
# /etc/profile or $HOME/.profile
export GOPATH=$HOME
export PATH=$PATH:$GOPATH/bin
Here is a rule
go install golang.org/dl/go[VERSION]@latest
go[VERSION] download
# VERSION = go version
Let's install go1.19.1
# Install go 1.19.1
go install golang.org/dl/go1.19.1@latest
go1.19.1 download
Downloaded 0.0% ( 16384 / 151222467 bytes) ...
Downloaded 7.9% ( 11878320 / 151222467 bytes) ...
Downloaded 21.0% ( 31702800 / 151222467 bytes) ...
Downloaded 26.1% ( 39403232 / 151222467 bytes) ...
Downloaded 39.4% ( 59620928 / 151222467 bytes) ...
Downloaded 53.1% ( 80297376 / 151222467 bytes) ...
Downloaded 60.4% ( 91307344 / 151222467 bytes) ...
Downloaded 74.4% (112573616 / 151222467 bytes) ...
Downloaded 89.1% (134691840 / 151222467 bytes) ...
Downloaded 100.0% (151222467 / 151222467 bytes)
Unpacking /Users/yoichika/sdk/go1.19.1/go1.19.1.darwin-amd64.tar.gz ...
Success. You may now run 'go1.19.1'
Let's run the installed go 1.19.1
# Check version
go1.19.1
go version go1.19.1 darwin/amd64
# Check GOROOT
go1.19.1 env GOROOT
/Users/yoichika/sdk/go1.19.1
# Check GOBIN
/Users/yoichika/dev/go/bin
# Run
go1.19.1 run main.go
If you want to use version 1.19.1 as go, you can create an alias or symlink (recommended) like this:
# alias
alias go="go1.19.1"
# symlink (recommended)
ln -s $GOPATH/bin/go1.19.1 $GOPATH/bin/go
How to uninstall the installed Go
# delete the directory ( go installed with installed command is stored on $HOME/sdk)
rm -rf $(go1.19.1 env GOROOT)
# delete /etc/paths.d/goを削除する
rm -rf /etc/paths.d/go
Tips: If you don't want to use brew-managed go, simply rename go symlink like this
which go
# /opt/homebrew/bin/go
ls -al /opt/homebrew/bin/go
# /opt/homebrew/bin/go -> ../Cellar/go/1.20.4/bin/go
mv /opt/homebrew/bin/go /opt/homebrew/bin/__go
ref
You may encounter the following compile error after you upgrade golang version
compile: version "go1.13.4" does not match go tool version "go1.14.14"
Check go env
and all variables point to right directories, especially GOPATH
and GOROOT
go env
GO111MODULE=""
GOARCH="amd64"
GOBIN="/Users/yoichika/dev/go/bin"
GOCACHE="/Users/yoichika/Library/Caches/go-build"
GOENV="/Users/yoichika/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/yoichika/dev/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/opt/go/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/opt/go/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/yoichika/dev/path-to-my-package/go.mod"
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/2s/06sdnhbn10x7rsv93p3047wc0000gn/T/go-build033280054=/tmp/go-build -gno-record-gcc-switches -fno-common"
In my case (MacOS), /usr/local/opt/go
should have been symlinked to new path like this:
$ ls -la /usr/local/opt/go
/usr/local/opt/go -> ../Cellar/go/1.13.4
$ rm /usr/local/opt/go
$ ln -s ../Cellar/go@1.14/1.14.14 /usr/local/opt/go
$ ls -la /usr/local/opt/go
/usr/local/opt/go -> ../Cellar/go@1.14/1.14.14
$ go env GOROOT
/usr/local/opt/go/libexec
export GOPATH=$HOME/dev/go
export PATH=$PATH:$GOPATH/bin
check $GOPATH
$ ls $GOPATH
bin pkg src
# Dependency Management
go get -d github.com/path/to/module # add or upgrade dep
go get -d github.com/dep/two/v2@v2.1.0 # use specific version
go get -d github.com/dep/commit@branch # use specific branch
go get -d -u ./... # upgrade all modules used in subdirs
go get -d github.com/dep/legacy@none # remove dep
# Useful commands
go mod tidy # organize and clean up go.mod and go.sum
go mod download # download deps into module cache
go mod init github.com/path/to/module # initialize new module
go mod why -m github.com/path/to/module # why is the module a dependency?
go install github.com/path/to/bin@latest # build and install a binary
-cflags -m
flag in building go allows to escape analysis and inlining (メモリ管理の分析) - ref Escape analysis and Inlining
C++と違いGOは内容に応じてheapとstackの利用を決定してくれる (ref GO言語のスタックとヒープ)
- 関数内だけで使われる値は、スタック
- 関数外でも値が必要になるなら、ヒープ
- newしてもスタックになることもある. 関数内だけで使われる値
- ローカル変数のアドレスを返しても良い. ヒープに置かれるようコンパイラが扱う
- インライン展開によって上手く最適化してくれる
- interfaceに代入するとヒープ
Simply execute go get
to update the module
cat go.mod
cloud.google.com/go v0.75.0
cloud.google.com/go/bigquery v1.15.0
github.com/gin-gonic/gin v1.6.3
github.com/go-playground/validator/v10 v10.2.0
github.com/gobwas/glob v0.2.3 // indirect
go get github.com/gin-gonic/gin
You'll see the updated version
cat go.mod
Finally run go mod tidy
# go get [-u] <package-name>
go mod tidy
ref: https://stackoverflow.com/questions/67201708/go-update-all-modules
3 dot
go [command] ./...
Here ./ tells to start from the current folder, ... tells to go down recursively.
example
# 再起的test実行
go test ./...
# 再起的go list
go list ./...
Install go package directory with go get
.
go get go.opencensus.io/examples/http/...
# [NOTE]: triple dot means install all sub packages as well
# See this for more detail: https://golang.org/cmd/go/
Useful packages:
# go-outline
go get -v github.com/ramya-rao-a/go-outline
# gocode
go get -v github.com/stamblerre/gocode
# gopkgs
go get -v github.com/uudashr/gopkgs/cmd/gopkgs
# go-symbols
go get -v github.com/acroca/go-symbols
# guru
go get -v golang.org/x/tools/cmd/guru
# gorename
go get -v golang.org/x/tools/cmd/gorename
# golint
go get -v golang.org/x/lint/golint
# godef
go get -v github.com/rogpeppe/godef
# goimports
go get -v golang.org/x/tools/cmd/goimports
Go 1.11
から(Versioned) Go Modules (a.k.a その実装であるvgo
)が利用できるようになっている。vgo
が有効化された go を使うと、 go build のタイミングで勝手に必要なモジュールバージョンをダウンロードしてくれる。 dep 等だと vendor に該当モジュールをダウンロードする前提だったけど、 vgo の場合はデフォルトでは vendor を利用しない。$GOPATH/go/mod 以下にバージョニングされた状態でダウンロードされて、vgo を利用する全プロジェクトはそこを参照するようになる Go 1.11ならGO111MODULES=on go build
のように go コマンドを実行すると利用できる。Go 1.12以降はデフォルトでGO111MODULES=auto
の扱いとなっている。auto の場合は 後述する GOPATH 以下のディレクトリの場合は vgo が無効化され、 GOPATH の外なら vgo が有効化された状態になる
# Go 1.11 -
export GO111MODULES=on
go build
# Go 1.12 - by default
export GO111MODULES=auto # auto の場合は 後述する GOPATH 以下のディレクトリの場合は vgo が無効化され、 GOPATH の外なら vgo が有効化された状態
(Reference: 他言語から来た人がGoを使い始めてすぐハマったこととその答え)
Go Modulesを使うときは、go.mod
ファイルをGoプログラムのルートディレクトリに置きます。 go.modファイルではモジュール名の宣言と、依存モジュールを記述することができます。go buildなどモジュールのimportが必要になれば$GOPATH/pkg/mod/
以下にバージョン別に整理されてダウンロードされ、参照できるようになります。 自分のプログラム内でimportするときもGOPATHの下で作業をしなくてもモジュール名を使ってimportができます。 つまり、Go ModulesではGOPATHの外で作業が可能、ただしライブラリキャッシュや go installコピー先はGOPATHの下になる)
(see Go modulesの話)
newmod
という名前のmoduleでプロジェクトを開始する
mkdir newmod
cd newmod
# go mod init <moduleName>
go mod init newmod
# -> go.mod ファイルが作られる
# Add module requirements and sums (go.modにrequiredsなモジュール追加)
go mod tidy
# xxx.goファイルを編集してBuildする
go build newmod
# -> go.modの中にrequireが必要なモジュールが記入される
例) サンプルで作ったmain.goをmodulesでやってみる
ls -1
# -> main.go
go mod init main
# -> go.modが作られる
go build main
# -> go.modにrequireなパッケージが記入される
cat go.mod
-------------------------
module main
go 1.13
require (
cloud.google.com/go/datastore v1.0.0
google.golang.org/api v0.13.0
)
-------------------------
go.mod
ファイルを生成するgo mod init
コマンドではdepで使われるGopkg.lock
ファイルがあればそれを解釈してgo.mod
に反映させる機能がある
すでにGopkg.lock
がある状態で
# GO111MODULE=on go mod init <modulename>
GO111MODULE=on go mod init mymod
Gopkg.lock
Gopkg.toml
README.md
go.mod <<<<<<新しく作られた
main.go
vendor
中身を覗いてみると
module main
go 1.12
require (
cloud.google.com/go v0.47.0
github.com/BurntSushi/toml v0.3.1
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9
github.com/golang/protobuf v1.3.2
github.com/googleapis/gax-go v1.0.3
github.com/jstemmer/go-junit-report v0.9.1
go.opencensus.io v0.22.1
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136
golang.org/x/lint v0.0.0-20190930215403-16217165b5de
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20191101200257-8dbcdeb83d3f
google.golang.org/api v0.13.0
google.golang.org/appengine v1.6.5
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6
google.golang.org/grpc v1.24.0
honnef.co/go/tools v0.0.1-2019.2.3
)
(see Go modulesの話)
From Go: module modeでのgo.mod, go.sumファイルを用いた依存ライブラリ管理、及びcontributionの方法&手順
# v1.19.12 という最新バージョンが存在している事が分かる
$ go list -u -m github.com/aws/aws-sdk-go
github.com/aws/aws-sdk-go v1.17.14 [v1.19.12]
# v1.18.x にマッチする最新バージョン、に更新してみる
$ go get github.com/aws/aws-sdk-go@'<v1.19'
go: finding github.com/aws/aws-sdk-go v1.18.6
go: downloading github.com/aws/aws-sdk-go v1.18.6
go: extracting github.com/aws/aws-sdk-go v1.18.6
# go.modの更新内容を確認
$ cat go.mod | grep aws-sdk-go
github.com/aws/aws-sdk-go v1.18.6
[補足] go get PACKAGE@VERSION - 指定バージョンを取得
go get foo@latest
- 現在取得可能な最新バージョンgo get foo@v1.6.2
- 指定したバージョンgo get foo@e3702bed2
- 指定したcommit hash(のリビジョン)go get foo@'<v1.6.2'
- 指定した条件に合致する最新バージョンgo get foo@master
- masterブランチの内容
From Go: module modeでのgo.mod, go.sumファイルを用いた依存ライブラリ管理、及びcontributionの方法&手順
go list -u -m all
- 全依存ライブラリに更新可能バージョン(があればそれ)を付けて一覧表示・確認go get -u
go build
orgo run
orgo test
を実行する(これでgo.mod, go.sumも最新化される)
- コードを編集・import文を削除
go mod tidy
go build
orgo run
orgo test
(これでgo.mod, go.sumも最新化される)
- go mod vendor
go.mod, go.sumがあるmodulesプロジェクトでgo mod vendor実行で依存パッケージがvendor
にコピーされる
例: go.modの内容をvendorに反映させて、go testでvendorを利用する
ls -la
go.mod go.sum ..
# 依存をvendorにコピー
go mod vendor
# vendorの内容でテストする
go test ./... -mod=vendor
Goでパッケージの依存関係を自動で解決して管理するソフトウェア. Goでは<パッケージのルート>/vendor/をimport pathとみなすことができるので、 vendor/ にパッケージを保存して特定のバージョンを固定して使うことができます(vendoring)。 プロジェクト固有のライブラリは $GOPATH/src/プロジェクト/vendor の下に入れても良いことになっている。そうすると go コマンドは vendor 以下を優先して見てくれる。面白い事に、vendor ディレクトリの管理は govendor 等色々なサードパーティツールを使う (see Go modulesの話)
NOTE: Go 1.12 からこのやり方を改め、$GOPATH/src や vendor は廃止となり Go Modules (vgo) という物を採用する事になった。Go 1.11の段階では移行期だが、環境変数 GO111MODULE (
export GO111MODULE=on
)により Go Modules を試す事が出来る
Install dep
using go get
go get -u github.com/golang/dep/cmd/dep
Create new project
mkdir $GOPATH/src/github.com/trydep
cd $GOPATH/src/github.com/trydep
# Manifest, Gopkg.toml and lock file, Gopkg.lock, and vendor directory will be created
dep init
Gopkg.toml
: application's dependancy listGopng.lock
vendor
: all packages are installed in this dir
How to use:
# inital kick to generate `Gopkg.toml` and `Gopkg.lock`
dep init
# install project's dependancies under `vender` directory
dep ensure
# add a dependancy to the project
dep ensure -add github.com/foo/bar
# update the locked versions of all dependencies
dep ensure -update
# update specified package
dep ensure -update github.com/foo/bar
# show current status
# dep status command give you info on projects dependencies and which packages are missing, etcx
dep status
Typical Project build using dep is like this:
kedacore/keda
go get -v github.com/kedacore/keda
cd $GOPATH/src/github.com/kedacore/keda
# install project's dependancies under `vender` directory
dep ensure
make build
LINKS
REPL
is Real-event-print loop
$ go get github.com/motemen/gore
$ gore -autoimport
gore version 0.3.0 :help for help
gore> fmt.Println("Hello World")
HelloHello WorldHello World
12
<nil>
source: https://github.com/motemen/gore
ghq
is a very helpful tool in putting go lang source codes under $GOPATH/src
.
source: https://github.com/motemen/ghq.
ghq
gets source code with the same rule as go get
and allows to show a list of all the repository under $GOPATH/src
with ghq list
.
# install
$ brew install ghq
# set ghq.root
$ git config --global ghq.root $GOPATH/src
Then, you're ready to use
$ ghq get owner/repo
$ ghq get https://github.com/owner/repo
$ ghq get git@git.example.com:path/to/repo.git
gopls
= official go language protocol server ( it's NOT lsp. it's also called 'Go Please')
gopls supports both Go module and GOPATH modes. However, it needs a defined scope in which language features like references, rename, and implementation should operate. Please read Setting up your workspace
:
Useful references on gopls
var n int
var f float64
var s string
var b bool
fmt.Printf("%#v %#v %#v %#v", n, f, s, b)
# -> 0 0 "" false
Go zero values would be like this
type | zero value |
---|---|
int,int8,int16,int32,int64 | 0 |
uint,uint8,uint16,uint32,uint64 | 0 |
byte,rune,uintptr | 0 |
float32,float64 | 0 |
complex64,complex128 | 0 |
bool | false |
string | "" |
error | nil |
fmt.Printf("%v", val)
: the value in a default format when printing structs, the plus flag (%+v) adds field namesfmt.Printf("%#v", val)
: a Go-syntax representation of the valuefmt.Printf("%T", val)
: a Go-syntax representation of the type of the value
for more detail, see: https://golang.org/pkg/fmt/
fmt.Printf("%#v", val)
でk8s node構造体をダンプした結果例
Node dump: v1.Node{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ObjectMeta:v1.ObjectMeta{Name:"ip-192-168-10-234.ap-northeast-1.compute.internal", GenerateName:"", Namespace:"", SelfLink:"/api/v1/nodes/ip-192-168-10-234.ap-northeast-1.compute.internal", UID:"f87d728b-3855-4347-b3fd-9446bc6a02d4", ResourceVersion:"865186", Generation:0, CreationTimestamp:v1.Time{Time:time.Time{wall:0x0, ext:63745067050, loc:(*time.Location)(0x2bec460)}}, DeletionTimestamp:(*v1.Time)(nil), DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"alpha.eksctl.io/cluster-name":"eks-test01", "alpha.eksctl.io/nodegroup-name":"standard-workers", "beta.kubernetes.io/arch":"amd64", "beta.kubernetes.io/instance-type":"m5.large", "beta.kubernetes.io/os":"linux", "eks.amazonaws.com/capacityType":"ON_DEMAND", "eks.amazonaws.com/nodegroup":"standard-workers", "eks.amazonaws.com/nodegroup-image":"ami-05d934c4ed7221f07", "eks.amazonaws.com/sourceLaunchTemplateId":"lt-062c3ffa7ac0825fb", "eks.amazonaws.com/sourceLaunchTemplateVersion":"1", "failure-domain.beta.kubernetes.io/region":"ap-northeast-1", "failure-domain.beta.kubernetes.io/zone":"ap-northeast-1c", "kubernetes.io/arch":"amd64", "kubernetes.io/hostname":"ip-192-168-10-234.ap-northeast-1.compute.internal", "kubernetes.io/os":"linux", "node.kubernetes.io/instance-type":"m5.large", "topology.kubernetes.io/region":"ap-northeast-1", "topology.kubernetes.io/zone":"ap-northeast-1c"}, Annotations:map[string]string{"node.alpha.kubernetes.io/ttl":"0", "volumes.kubernetes.io/controller-managed-attach-detach":"true"}, OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ClusterName:"", ManagedFields:[]v1.ManagedFieldsEntry{v1.ManagedFieldsEntry{Manager:"kube-controller-manager", Operation:"Update", APIVersion:"v1", Time:(*v1.Time)(0xc0002ea800), FieldsType:"FieldsV1", FieldsV1:(*v1.FieldsV1)(0xc0002ea820)}, v1.ManagedFieldsEntry{Manager:"kubelet", Operation:"Update", APIVersion:"v1", Time:(*v1.Time)(0xc0002ea8a0), FieldsType:"FieldsV1", FieldsV1:(*v1.FieldsV1)(0xc0002ea8c0)}}}, Spec:v1.NodeSpec{PodCIDR:"", PodCIDRs:[]string(nil), ProviderID:"aws:///ap-northeast-1c/i-009af4df315f8acbb", Unschedulable:false, Taints:[]v1.Taint(nil), ConfigSource:(*v1.NodeConfigSource)(nil), DoNotUseExternalID:""}, Status:v1.NodeStatus{Capacity:v1.ResourceList{"attachable-volumes-aws-ebs":resource.Quantity{i:resource.int64Amount{value:25, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"25", Format:"DecimalSI"}, "cpu":resource.Quantity{i:resource.int64Amount{value:2, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"2", Format:"DecimalSI"}, "ephemeral-storage":resource.Quantity{i:resource.int64Amount{value:85886742528, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"83873772Ki", Format:"BinarySI"}, "hugepages-1Gi":resource.Quantity{i:resource.int64Amount{value:0, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"0", Format:"DecimalSI"}, "hugepages-2Mi":resource.Quantity{i:resource.int64Amount{value:0, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"0", Format:"DecimalSI"}, "memory":resource.Quantity{i:resource.int64Amount{value:8141807616, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"", Format:"BinarySI"}, "pods":resource.Quantity{i:resource.int64Amount{value:29, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"29", Format:"DecimalSI"}}, Allocatable:v1.ResourceList{"attachable-volumes-aws-ebs":resource.Quantity{i:resource.int64Amount{value:25, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"25", Format:"DecimalSI"}, "cpu":resource.Quantity{i:resource.int64Amount{value:1930, scale:-3}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"1930m", Format:"DecimalSI"}, "ephemeral-storage":resource.Quantity{i:resource.int64Amount{value:76224326324, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"76224326324", Format:"DecimalSI"}, "hugepages-1Gi":resource.Quantity{i:resource.int64Amount{value:0, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"0", Format:"DecimalSI"}, "hugepages-2Mi":resource.Quantity{i:resource.int64Amount{value:0, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"0", Format:"DecimalSI"}, "memory":resource.Quantity{i:resource.int64Amount{value:7435067392, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"", Format:"BinarySI"}, "pods":resource.Quantity{i:resource.int64Amount{value:29, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"29", Format:"DecimalSI"}}, Phase:"", Conditions:[]v1.NodeCondition{v1.NodeCondition{Type:"MemoryPressure", Status:"False", LastHeartbeatTime:v1.Time{Time:time.Time{wall:0x0, ext:63745322450, loc:(*time.Location)(0x2bec460)}}, LastTransitionTime:v1.Time{Time:time.Time{wall:0x0, ext:63745067048, loc:(*time.Location)(0x2bec460)}}, Reason:"KubeletHasSufficientMemory", Message:"kubelet has sufficient memory available"}, v1.NodeCondition{Type:"DiskPressure", Status:"False", LastHeartbeatTime:v1.Time{Time:time.Time{wall:0x0, ext:63745322450, loc:(*time.Location)(0x2bec460)}}, LastTransitionTime:v1.Time{Time:time.Time{wall:0x0, ext:63745067048, loc:(*time.Location)(0x2bec460)}}, Reason:"KubeletHasNoDiskPressure", Message:"kubelet has no disk pressure"}, v1.NodeCondition{Type:"PIDPressure", Status:"False", LastHeartbeatTime:v1.Time{Time:time.Time{wall:0x0, ext:63745322450, loc:(*time.Location)(0x2bec460)}}, LastTransitionTime:v1.Time{Time:time.Time{wall:0x0, ext:63745067048, loc:(*time.Location)(0x2bec460)}}, Reason:"KubeletHasSufficientPID", Message:"kubelet has sufficient PID available"}, v1.NodeCondition{Type:"Ready", Status:"True", LastHeartbeatTime:v1.Time{Time:time.Time{wall:0x0, ext:63745322450, loc:(*time.Location)(0x2bec460)}}, LastTransitionTime:v1.Time{Time:time.Time{wall:0x0, ext:63745067070, loc:(*time.Location)(0x2bec460)}}, Reason:"KubeletReady", Message:"kubelet is posting ready status"}}, Addresses:[]v1.NodeAddress{v1.NodeAddress{Type:"InternalIP", Address:"192.168.10.234"}, v1.NodeAddress{Type:"ExternalIP", Address:"3.112.60.73"}, v1.NodeAddress{Type:"Hostname", Address:"ip-192-168-10-234.ap-northeast-1.compute.internal"}, v1.NodeAddress{Type:"InternalDNS", Address:"ip-192-168-10-234.ap-northeast-1.compute.internal"}, v1.NodeAddress{Type:"ExternalDNS", Address:"ec2-3-112-60-73.ap-northeast-1.compute.amazonaws.com"}}, DaemonEndpoints:v1.NodeDaemonEndpoints{KubeletEndpoint:v1.DaemonEndpoint{Port:10250}}, NodeInfo:v1.NodeSystemInfo{MachineID:"ec2a306cc17f479e42954c0ed6d71f8d", SystemUUID:"EC2A306C-C17F-479E-4295-4C0ED6D71F8D", BootID:"677dff8e-7a9e-4fcf-b9a3-e1f0f7e2bfeb", KernelVersion:"4.14.209-160.335.amzn2.x86_64", OSImage:"Amazon Linux 2", ContainerRuntimeVersion:"docker://19.3.6", KubeletVersion:"v1.18.9-eks-d1db3c", KubeProxyVersion:"v1.18.9-eks-d1db3c", OperatingSystem:"linux", Architecture:"amd64"}, Images:[]v1.ContainerImage{v1.ContainerImage{Names:[]string{"docker.elastic.co/elasticsearch/elasticsearch@sha256:3ad224719013a016ab4931d1891fcf873fdf6b8c38d0970e30fc1c8b0c07f436", "docker.elastic.co/elasticsearch/elasticsearch:7.10.0"}, SizeBytes:773720806}, v1.ContainerImage{Names:[]string{"602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni@sha256:f310c918ee2b4ebced76d2d64a2ec128dde3b364d1b495f0ae73011f489d474d", "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni:v1.7.5-eksbuild.1"}, SizeBytes:312076970}, v1.ContainerImage{Names:[]string{"602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init@sha256:d96d712513464de6ce94e422634a25546565418f20d1b28d3bce399d578f3296", "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init:v1.7.5-eksbuild.1"}, SizeBytes:287782202}, v1.ContainerImage{Names:[]string{"602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy@sha256:71b15b05cdee85a1ff6bdb5bdf9c0788c5089cc72b2f0f41a666c22488670ea4", "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy:v1.18.8-eksbuild.1"}, SizeBytes:131606723}, v1.ContainerImage{Names:[]string{"602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/coredns@sha256:edffc302f7adb1adc410d45ed2266d83c966bc195995191da63e035111e38afd", "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/coredns:v1.7.0-eksbuild.1"}, SizeBytes:46269990}, v1.ContainerImage{Names:[]string{"602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause@sha256:1cb4ab85a3480446f9243178395e6bee7350f0d71296daeb6a9fdd221e23aea6", "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause:3.1-eksbuild.1"}, SizeBytes:682696}}, VolumesInUse:[]v1.UniqueVolumeName{"kubernetes.io/aws-ebs/aws://ap-northeast-1c/vol-056f0c4c4eda394a9", "kubernetes.io/aws-ebs/aws://ap-northeast-1c/vol-09df3f95e9992955b"}, VolumesAttached:[]v1.AttachedVolume{v1.AttachedVolume{Name:"kubernetes.io/aws-ebs/aws://ap-northeast-1c/vol-09df3f95e9992955b", DevicePath:"/dev/xvdbj"}, v1.AttachedVolume{Name:"kubernetes.io/aws-ebs/aws://ap-northeast-1c/vol-056f0c4c4eda394a9", DevicePath:"/dev/xvdch"}}, Config:(*v1.NodeConfigStatus)(nil)}}
github.com/davecgh/go-spew/spew give you more human readable format
import (
"github.com/davecgh/go-spew/spew"
)
func main() {
spew.Dump(node)
}
var (
i int = 1 // can omit int as it will be inferred from the light-hand value
f64 float64 = 1.2 // can omit float64 as it will be inferred from the light-hand value
s string = "test" // can omit string as it will be inferred from the light-hand value
t, f bool = true, false
)
xi := 1
xi = 2
xf64 := 1.2
var xf32 float32 = 1.2
xs := "test"
xt, xf := true, false
fmt.Println(xi, xf64, xs, xt, xf)
// %T show variable type
fmt.Printf("%T\n", xf64)
fmt.Printf("%T\n", xf32)
import (
"fmt"
"strings"
"strconv"
)
fmt.Println("Hello " + "World") // Hello World
fmt.Println(string("Hello World"[0])) //H
s := "Hello World"
s = strings.Replace(s, "H", "Y", 1) // Yello World
fmt.Println(strings.Contains(s, "Hello")) //trude
fmt.Println(`AAA
xxxx BBB
m (_ _)m
CCC`)
fmt.Println(`"`)
fmt.Println("\"")
// Convert
i := 8
x := fmt.Sprintf("k%ds", i)
fmt.Println(x) // k8s
// strconv.Atoi(string) int, error
i, _ = strconv.Atoi("100")
fmt.Println(i) // 100
// strconv.Itoa(int) string
fmt.Println(strconv.Itoa(100)) // 100
// substring
str3 := "hello"
for i := 0; i < len(str3); i++ {
fmt.Println(str3[i : i+1])
}
A rune is an alias to the int32 data type. It represents a Unicode code point. A Unicode code point or code position is a numerical value that is usually used to represent a Unicode character. The int32 is big enough to represent the current volume of 140,000 unicode characters. https://zetcode.com/golang/rune/
a1 := '🦍'
var a2 = 'k'
var a3 rune = '🦡'
fmt.Printf("%c - %s\n", a1, reflect.TypeOf(a1))
fmt.Printf("%c - %s\n", a2, reflect.TypeOf(a2))
fmt.Printf("%c - %s\n", a3, reflect.TypeOf(a3))
// char array to string
fmt.Println("char array (rune array) to string")
chars := []rune{'h', 'e', 'l', 'l', 'o'}
str := string(chars)
fmt.Println(str) // hello
// string to char array
// pass string to []rune(str). This will create a slice of runes where each character is stored as an element in the resulting slice.
fmt.Println("string to char array (rune array)")
str2 := "abc"
chars2 := []rune(str2)
for i := 0; i < len(chars2); i++ {
char := string(chars2[i])
fmt.Println(char)
}
fmt.Println("string range loop")
str3 := "abcdefghijklmnopqrstuvwxyz"
for _, v := range str3 { // v = rune
var x rune = v
fmt.Printf("c:%c d:%d c-'a':%d\n", x, x, x-'a')
}
// Alphabete chars can be save in [26]int
/*
c:a d:97 c-'a':0
c:b d:98 c-'a':1
c:c d:99 c-'a':2
c:d d:100 c-'a':3
c:e d:101 c-'a':4
c:f d:102 c-'a':5
c:g d:103 c-'a':6
c:h d:104 c-'a':7
c:i d:105 c-'a':8
c:j d:106 c-'a':9
c:k d:107 c-'a':10
c:l d:108 c-'a':11
c:m d:109 c-'a':12
c:n d:110 c-'a':13
c:o d:111 c-'a':14
c:p d:112 c-'a':15
c:q d:113 c-'a':16
c:r d:114 c-'a':17
c:s d:115 c-'a':18
c:t d:116 c-'a':19
c:u d:117 c-'a':20
c:v d:118 c-'a':21
c:w d:119 c-'a':22
c:x d:120 c-'a':23
c:y d:121 c-'a':24
c:z d:122 c-'a':25
*/
- Difference between Array and Slice
- Array = fix number of element
- Slice = dynamic number of element. Can make slice with
s := []int{1, 2}
ors := make([]int, len, cap)
. Expand slice withappend
- Sliceは参照型なので関数に渡してもコピーが発生しない。一方、Arrayは値型なので参照渡ししなければコピーが作られてしまい非効率
- Return
_
: declear I don't use this
// Array
var a[2]int // You must give the SIZE to array
a[1]=10
a[2]=20
// var a = [2]int{10,20}
// a := [...]int{10, 20} // elipsis -> Compiler figures out array length
var a[3] = [3]int{1,2,3}
// cannot append as you can NOT change the SIZE of array
// ar = append(ar, 4)
//Slice
//var s []int
//var s = []int {1, 2}
s := []int{1,2} // This is how you define Slice
s[0]=10
s[1]=20
s = append(s, 3) // You CAN change the size of slice
fmt.Println(s)
// you can add slice in slice
var sb = [][]int{
[]int{0, 1, 2},
[]int{3, 4, 5},
[]int{6, 7, 8},
}
// Slice basic
n := []int{1, 2, 3, 4, 5, 6}
fmt.Println(n)
fmt.Println(n[2]) // 2nd
fmt.Println(n[2:4]) // 2nd-> 4thまで、でも4thを含まない -> [3 4]
fmt.Println(n[:2]) // 2ndまで、でも2ndを含まない -> [1,2]
fmt.Println(n[2:]) // 2nd から全部 -> [3,4,5,6]
fmt.Println(n[:]) // all
// Struct slice
s := []struct {
a string
b string
}{
{"a1", "b1"},
{"a2", "b2"},
{"a3", "b3"},
{"a4", "b4"},
}
for _, v := range s {
fmt.Printf("a:%s b:%s \n", v.a, v.b)
}
Here is how you iterate arrays and slices
a := [4]int{10, 20, 30, 40}
for i := 0; i < len(a); i++ {
fmt.Printf("basic loop: index=%d val=%d\n", i, a[i])
}
// loop over an array/a slice
for i, e := range a {
// i is the index, e the element
fmt.Printf("index=%d val=%d\n", i, e)
}
// if you only need e:
for _, e := range a {
// e is the element
fmt.Printf("val=%d\n", e)
}
// ...and if you only need the index
for i := range a {
fmt.Printf("index=%d\n", i)
}
// In Go pre-1.4, you'll get a compiler error if you're not using i and e.
// Go 1.4 introduced a variable-free form, so that you can do this
for range time.Tick(time.Second) {
// do it once a sec
fmt.Println("do it once a second")
}
you can create dynamically-sized arrays
using make([]type, length, capacity)
where cap
tells you the capacity of the underlying array, and len
tells you how many items are in the array.
SliceのCapacityは拡張が可能 スライスの要素数が配列を下回っている場合は配列をそのまま利用できますが、スライスの要素数が配列を超える場合は配列自体を作り直す必要がある。 これをallocationという。 ただし、allocationの発生を抑えてパフォーマンスを向上させるためには、スライスの初期化時にキャパシティをある程度確保することも検討しましょう。 ref: https://note.crohaco.net/2019/golang-slice/
x := make([]int, 0, 5) // len(b)=0, cap(b)=5
fmt.Printf("len=%d, capacity=%d value=%v\n", len(n), cap(n), n) // len=0, cap=5 value=[]
n := make([]int, 3, 5)
fmt.Printf("len=%d, cap=%d value=%v\n", len(n), cap(n), n) // len=3, cap=5 value=[0 0 0]
n = append(n, 0, 0)
fmt.Printf("len=%d, cap=%d value=%v\n", len(n), cap(n), n) // len=5, cap=5 value=[0 0 0 0 0]
// Capacityは5ー>10 に拡張された
// メモリ領域を気にするならばCapに収めること。ただしダイナミックにallocateすることは可能
n = append(n, 1, 2, 3, 4, 5)
fmt.Printf("len=%d, cap=%d value=%v\n", len(n), cap(n), n) // len=10, cap=10 value=[0 0 0 0 0 1 2 3 4 5]
a := make([]int, 3) // len = cap
fmt.Printf("len=%d, capacity=%d value=%v\n", len(a), cap(a), a) // len=3, cap=3 value=[0 0 0]
fmt.Println("diff between dynamic and static")
b := make([]int, 0) // len = capで、0のスライスをメモリに確保
fmt.Printf("len=%d, capacity=%d value=%v\n", len(b), cap(b), b) // len=0, cap=0 value=[]
b = append(b,0,0) // appendでslice拡張 -> allocation
fmt.Printf("len=%d, capacity=%d value=%v\n", len(b), cap(b), b) // len=2, cap=2 value=[0 0]
var c []int // nil としてメモリに確保しない
fmt.Printf("len=%d, capacity=%d value=%v\n", len(c), cap(c), c) // len=0, cap=0 value=[]
c = append(c,0,0) // appendでslice拡張 -> allocation
fmt.Printf("len=%d, capacity=%d value=%v\n", len(c), cap(c), c) // len=2, cap=2 value=[0 0]
// Basic
// var m map[string]int
m := map[string]int{"apple": 100, "banana": 200}
m["banana"] = 300
fmt.Println(m["banana"]) // -> 300
fmt.Println(m["nothing"]) // -> 0
// check if the value exists
v2, ok2 := m["nothing"]
fmt.Println(v2, ok2) // -> 0, false
// make: メモリ上に空のMapを作成する
m2 := make(map[string]int)
m2["pc"] = 200
fmt.Println(m2["pc"])
/*
var m3 map[string]int
m3["pc"] = 5000
fmt.Println(m3)
->>>> panic ::: 宣言はしているけどメモリ上にMapがないのでNilなのでPanic発生
// varで宣言したときはMapもSliceもNilなので注意!!
var s []int
if s == nil {
fmt.Println("Nil")
}
*/
// iterate over map
for k, v := range m {
fmt.Printf("key=%s val=%d\n", k, v)
}
type Person struct {
Name string
Age int
}
m3 := map[string]Person{
"Taro": {"Go Taro", 35},
"Jiro": {"Go Jiro", 30},
}
for k, v := range m3 {
fmt.Printf("key=%s Person: name=%s age=%d\n", k, v.Name, v.Age)
}
For loop
for i:=0; i < 10; i++ {
fmt.Println(i)
}
for a && b {
fmt.Println("use like while")
}
Iterate over an array/a slice
for i, e := range a {
// i is the index, e the element
fmt.Printf("index=%d val=%d\n", i, e)
}
// if you only need e:
for _, e := range a {
// e is the element
fmt.Printf("val=%d\n", e)
}
// ...and if you only need the index
for i := range a {
fmt.Printf("index=%d\n", i)
}
Iterate over map
for k, v := range m {
fmt.Printf("key=%s val=%d\n", k, v)
}
Many ways to sort in Go https://yourbasic.org/golang/how-to-sort-in-go/
import (
"fmt"
"sort"
)
func main() {
//----------------------------------------
// Sort slice of ints
//----------------------------------------
ilist := []int{4, 2, 3, 1}
sort.Ints(ilist)
fmt.Println(ilist) // [1 2 3 4]
//----------------------------------------
// sort slice of floats
//----------------------------------------
flist := []float64{5.2, -1.3, 0.7, -3.8, 2.6} // unsorted
sort.Float64s(flist)
fmt.Println(flist) // [-3.8 -1.3 0.7 2.6 5.2]
//----------------------------------------
// Sort slice of strings
//----------------------------------------
slist := []string{"a", "xx", "c", "dd", "bb"}
sort.Strings(slist)
fmt.Println(slist) // [a bb c dd xx]
//----------------------------------------
// Sort with custom comparator
//----------------------------------------
family := []struct {
Name string
Age int
}{
{"Yoichi", 23},
{"Kawasaki", 45},
{"Yamazaki", 33},
{"Kitano", 57},
}
sort.SliceStable(family, func(i, j int) bool {
return family[i].Age < family[j].Age
})
fmt.Println(family) // [{Yoichi 23} {Yamazaki 33} {Kawasaki 45} {Kitano 57}]
//----------------------------------------
// Sort a map by key or value
//----------------------------------------
m := map[string]int{ "Yoichi": 23, "Kawasaki": 45, "Yamazaki": 33, "Kitano": 57, }
keys := make([]string, 0, len(m))
for k, _ := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
//-> Kawasaki 45, Kitano 57, Yamazaki 33, Yoichi 23
}
// You can return multiple values
func testfunc(x, y int) (int, int) {
return x + y, x - y
}
// Named return value
func testfunc(a, b int) (result int) {
result = a * b
//return result ## This is also OK
return // Naked Returns
}
// Inner func
f := testfunc(x int) {
fmt.Println("Inner func", x)
}
f(1)
// You can directry execute the func instead of assigning it to f
func(x int) {
fmt.Println("Inner func", x)
}(1)
// Variadic parameters
func varparamfunc(params ...int) {
for _, p := range params {
fmt.Println(p)
}
}
varparamfunc(1, 2)
varparamfunc(1, 2, 3)
params := []int{1, 2, 3, 4}
varparamfunc(params...)
// Func as return value
func incrCounter() func() int {
x := 0
return func() int {
fmt.Println("inner func: Increment")
x++
return x
}
}
counter := incrCounter()
fmt.Println(counter())
fmt.Println(counter())
fmt.Println(counter())
// struct atrribute:
// CAPTIAL -> Accessible from outside
// NOT CAPITAL -> NOT accessible from outside
type Vertex struct {
X, Y int
Z string
}
// There is NO class in Golang but use Struct for OOP like
// Value vs Pointer?
// - Value -> When you refer to the value
// - Pointer -> When you change the value
// Value receiver
func (v Vertex) UseValue() string {
return fmt.Sprintf("X=%d Y=%d Z=%s", v.X, v.Y, v.Z)
}
// Pointer receiver
func (v *Vertex) ChangeValue(i int) {
v.X = v.X * i
v.Y = v.Y * i
v.Z = fmt.Sprintf("%s_%d", v.Z, i)
}
// Newパターン: Goデザインパターン
// https://golang.org/search?q=New
func New(x, y int, z string) *Vertex {
return &Vertex{x, y, z}
}
func main(){
v1 := Vertex{X: 1, Y: 2, Z:"hoge"}
v2 := Vertex{X: 1}
fmt.Println(v2) // -> {1 0 }
v3 := Vertex{1, 2, "hoge"}
fmt.Println(v3) // -> {1 2 hoge}
v4 := Vertex{}
var v5 Vertex
fmt.Printf("%T %v\n", v5, v5) // -> main.Vertex {0 0 }
v6 := new(Vertex)
fmt.Printf("%T %v\n", v6, v6) // -> *main.Vertex &{0 0 }
v7 := &Vertex{1, 2, "hoge"}
fmt.Printf("%T %v\n", v7, v7)
// [Key Point]
// It's very common to use pointer for struct in dealing struct in your app
// while it's recommended to use make for Slice / Map
v := Vertex{100, 200, "foo"}
fmt.Println(v.UseValue()) // X=100 Y=200 Z=foo
v.ChangeValue(100)
fmt.Println(v.UseValue()) // X=10000 Y=20000 Z=foo_10
// Constructor by using New pattern (Go design pattern)
v8 := New(3,4, "hoge")
v8.ChangeValue(100)
fmt.Printf("%T %v\n", v8, v8)
}
func set_ten(x *int) {
*x = 10
}
var n = 100
set_ten(&n)
fmt.Println(n) // -> 10
fmt.Println(&n) // -> address
var p *int = &n
fmt.Println(p) // -> address
fmt.Println(*p) // -> 10
fmt.Println(&*p) // -> address
Dynamic Memory Aloocation with New Go言語には、Javaの様なGCが組み込まれているため確保したメモリを利用者が解放する必要はない newで割り当てたメモリは、変数宣言と同様に、型ごとのゼロ値で初期化される
// int type
var p *int = new(int)
*p = 0
called(a, p)
fmt.Println("int pointer value", *p) // int pointer value 1
// struct type
type myStruct struct {
a int
b int
}
var my *myStruct = new(myStruct)
called((*my).a, &(*my).b)
fmt.Println("pointer value: struct.a, struct.b=", (*my).a, (*my).b) // struct pointer value: struct.a, struct.b= 0 1
// a - 値渡し
// b - ポインタ渡し or 参照渡し
func called(a int, b *int) {
a = a + 1
*b = *b + 1
}
Slice pointer
func compareStringAndSwitch(i1 int, i2 int, list *[]string) {
if i1 > len(*list)-1 || i2 > len(*list)-1 || i1 > i2 {
fmt.Printf("skip: i1(%d), i2(%d)\n", i1, i2)
return
}
s1 := (*list)[i1] // slice pointerの実体参照
s2 := (*list)[i2] // slice pointerの実体参照
n1, err1 := strconv.Atoi(s1)
n2, err2 := strconv.Atoi(s2)
if err1 != nil || err2 != nil {
fmt.Println("invalid array value")
return
}
if n1 > n2 { // compare and switch
(*list)[i1] = s2
(*list)[i2] = s1
}
}
data := []string{"10", "1", "2", "8", "4", "3", "9", "7", "6", "5"}
for idx, _ := range data {
for i := 0; i < idx; i++ {
compareStringAndSwitch(i, idx, &data)
}
}
fmt.Println(data) // [1 2 3 4 5 6 7 8 9 10]
Basically use pointer receiver, but use value receiver in case :
- you don't need to change the value of receiver
- struct is based on primitive data type as copy cost for primitive type is very low
type Human struct{ Name string }
// Value receiver
func (h Human) Say(something string) {
fmt.Printf("%s said %s.\n", h.Name, something)
}
// Value receiver
func (h Human) Change(name string) {
h.Name = name
}
// Pointer receiver
func (h *Human) SayP(something string) {
fmt.Printf("%s said %s.\n", h.Name, something)
}
// Pointer receiver
func (h *Human) ChangeP(name string) {
h.Name = name
}
hp := &Human{Name: "Yoichi"}
hp.SayP("Hello")
hp.Say("Hello") //コンパイラによるレシーバの暗黙的変換
hp.ChangeP("Kawasaki") //レシーバの値を変更
hp.SayP("Hello")
h := &Human{Name: "Yoichi"}
h.SayP("Hello")
h.Say("Hello") //コンパイラによるレシーバの暗黙的変換
h.Change("Kawasaki") //レシーバの値が変更されない
h.Say("Hello")
See also this, good summary!
Interfaceで宣言したメソッドを必ず実装してほしいときにInterfaceを使う
// Interface definition
type Car interface {
Run() string
}
type SuperCar struct {
Name string
}
// Implimentation of method
func (s *SuperCar) Run() string {
return s.Name + " is running very fast"
}
var car1 Car = &SuperCar{"Ferrari"}
fmt.Println(car1.Run())
Empty interfaceについて。 Empty Interfaceにはどんな型でも代入することが可能
type Hoge interface{} // 空インターフェース定義
var hoge Hoge
// var hoge interface{} // 空インターフェース定義SHORTCUT
// You can give any value
hoge = 0
hoge = "hey"
//"value, ok = element.(T)" => valueは変数の値が格納、okにはbool型
if value, ok := hoge.(int); ok {
fmt.Printf("int value:%d", value)
} else if value, ok := hoge.(string); ok {
fmt.Printf("string value:%s", value)
} else {
fmt.Println("other")
}
/******************************
Goroutine keypoints
var wg sync.WaitGroup
go gorouine(xxx, &wg) //goroutine(xx, wg *sync.WaitGroup)
wg.Add(N) // incr N thread operation
wg.Done() x N // Done when thread done (if it's in a scope call wg.Done() with defer)
wg.Wait() // end of goroutine (goroutine wait until wg.done(). Without this, main func go end before goroutine)
******************************/
import ( "sync")
func goroutine(s string, wg *sync.WaitGroup) {
defer wg.Done()
// do something
// wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(1) // tell that I have 1 thread operation -> Wait 1 wg.Done()
go goroutine("world", &wg)
wg.Wait() // Wait wg.Done()
}
Channels
are the pipes
that connect concurrent goroutines
. By default, channlels are unbuffered
: accept 1 send(c<-) if a receive(<-c)
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
Buffered Channels
allow you to receive N send(c<-) without N receive(<-c)
msg := make(chan string, 2) //buffered channel
msg <- "buf buf" //send
msg <- "chan chan" //send
fmt.Println(<-msg) //receive
fmt.Println(<-msg) //receive
// Close Channel so no longer channel send & receive msg
ch := make(chan int, 2)
ch <- 100
ch <- 200
fmt.Println(len(ch))
close(ch)
// Without close channle, you'll get error in looping with Range as Range try to get 3rd one
for c := range ch {
fmt.Println(c)
}
Channel Syncronization
- synchronize execution (block recieve to wait for a goroutine to finish) across goroutines
func goroutine(done chan bool) {
// Do something
// Send a value to notify that we're done.
done <- true
}
func main() {
// Start a worker goroutine, giving it the channel to notify on.
done := make(chan bool, 1)
go goroutine(done)
// Block until receive on the channel.
<-done
}
Use select
when you don't want to block receive (Channel block receive by default)
select {
case v := <- ch:
fmt.Println(v)
default:
fmt.Println("no value")
}
// if channle is aloready closed, channle not block and so you'll get value in "case v := <-ch"
// you can identify if you can receive or channle closeed, do like this
select {
case v, ok := <- ch:
if ok {
fmt.Println(v)
} else {
fmt.Println("closed")
}
default:
fmt.Println("no value")
}
Run Go: Install/Update Tools
in Command Palette to update all tools
"go.autocompleteUnimportedPackages": true,
// ツール類を最新にするだけではパッケージが見つからない
"go.toolsEnvVars": {"GO111MODULE": "on"},
// go.formatToolのデフォルト設定goreturnsはModulesに対応していない様子
"go.formatTool": "goimports"
"[go]": {
// Save時にGoフォーマットするかどうか
"editor.formatOnSave": true
}
- 補足:
go.gopaths
は不要。もし入れるならばフルパスで入れる。$HOME/dev/go
の形式はうまく読み込めない - https://qiita.com/msmsny/items/a8d4573d03774815a198
- Standard Go Project Layout
- Golang Coding Style