Skip to content

Instantly share code, notes, and snippets.

@yokawasa
Last active November 26, 2023 03:21
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yokawasa/fc8ada72859569a60179c5414423f78f to your computer and use it in GitHub Desktop.
Save yokawasa/fc8ada72859569a60179c5414423f78f to your computer and use it in GitHub Desktop.
Golang Cheat Sheet

Golang Cheat Sheet

Fundarmental

  • 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

Golang Installation

Single version

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

Multiple versions

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

Troubleshoot on golang version upgrade

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

GET GOROOT

$ go env GOROOT

/usr/local/opt/go/libexec

GOPATH

export GOPATH=$HOME/dev/go
export PATH=$PATH:$GOPATH/bin

check $GOPATH

$ ls $GOPATH

bin pkg src

Go Module

Go Module Cheat Sheet

# 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に代入するとヒープ

How to update go.mod

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

Tips

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 ./... 

Tools

go get

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

vgo (a.k.a Modules)

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
)
-------------------------

depからの移行

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の話)

ライブラリのMINORバージョン又はPATCHバージョンを更新

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ブランチの内容

全ライブラリを最新versionに更新する手順

From Go: module modeでのgo.mod, go.sumファイルを用いた依存ライブラリ管理、及びcontributionの方法&手順

  • go list -u -m all - 全依存ライブラリに更新可能バージョン(があればそれ)を付けて一覧表示・確認
  • go get -u
  • go build or go run or go test を実行する(これでgo.mod, go.sumも最新化される)

使わなくなったライブラリを削除する手順

  • コードを編集・import文を削除
  • go mod tidy
  • go build or go run or go test (これでgo.mod, go.sumも最新化される)

go.modの内容をvendorに反映させる

  • 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

dep

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 list
  • Gopng.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

gore - Go REPL

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

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

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

Go Language Rules

ZERO Values

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

ゼロ値を使おう

Debug Print

  • fmt.Printf("%v", val): the value in a default format when printing structs, the plus flag (%+v) adds field names
  • fmt.Printf("%#v", val): a Go-syntax representation of the value
  • fmt.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) 
}

Variables

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)

String

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])
}

rune

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
*/

Array and Slice

  • Difference between Array and Slice
    • Array = fix number of element
    • Slice = dynamic number of element. Can make slice with s := []int{1, 2} or s := make([]int, len, cap). Expand slice with append
    • 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]

Map

// 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)
}

Loop

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)
}

Sort

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
}

Function

// 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...)

Closure

// 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

// 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)
}

Pointer

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] 

Pointer receiver and Value receiver

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を使う

// 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

/****************************** 
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()
}

Channel

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")
}

golang with VSCode

Install/Update Tools

Run Go: Install/Update Tools in Command Palette to update all tools

Go config in settings.json

"go.autocompleteUnimportedPackages": true,
// ツール類を最新にするだけではパッケージが見つからない
"go.toolsEnvVars": {"GO111MODULE": "on"},
// go.formatToolのデフォルト設定goreturnsはModulesに対応していない様子
"go.formatTool": "goimports"

"[go]": {
  // Save時にGoフォーマットするかどうか
  "editor.formatOnSave": true
}

Golang Coding Style

LINKS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment