golang チートシート

More than 3 years have passed since last update.

自分が学習した内容の記録。少しずつ記述を増やしていく。

A Tour of Go

A Tour of Go(日本語)

を先に読むこと。

以下も、必読である。

実践Go言語

Goプログラミング言語仕様

以降の記述でmain()の定義など冗長な部分は省略する。適宜読み取って補完すること。


ダウンロード

Download Go


Hello World


hello.go

package main

import "fmt"

func main() {
fmt.Printf("Hello, world.\n")
}


コンパイル

go build hello.go   # コマンドhelloが作成される

実行

./hello

Hello, World.

コンパイル&実行

go run hello.go   // 実行ファイルをtmpディレクトリに生成して実行

Hello, World.


言語の特徴

特徴
説明

コンパイル言語

文字の大小
区別する

式(expression)と文(statement)
区別あり式を要求する場所に文は書けない。

メモリ管理
ガーベージコレクション(mark&sweep)

else if
else if

++, --
あり。ただし後置のみ。文なので式に書けない。


バージョン確認

go version


コメント

コメント形式
コメント範囲

行コメント

// から改行まで

ブロックコメント

/* から */ まで(ネスト不可)


変数

foo

Bar
BAZ

先頭が大文字かどうかは意味がある(大文字の場合、パッケージ外にエクスポートされる)

使用されない変数宣言は構文エラーになる。関数の戻り値に使用しない値がある場合は、

"_" に代入する。単独の "_"(blank identifier) は変数ではない

_, err := foo()

// 定義

var x int
var x int, y int
var x, y int
var (
x int
y int
)

// 初期化子(initializer)
// 初期化子があれば型を省略できる

var x, y int = 1, 2
var x, y = 1, 2

// 関数の中なら := による変数定義もできる
x, y := 1, 2


定数

character、string、boolean、数値(numeric)のみ(コンパイル時に評価される)

const Pi = 3.14

括弧()で括られたconst宣言では、最初の式と同じ値が各定数に設定される

const (

A = 1
B
C = 2
)
fmt.Println(A) // => 1
fmt.Println(B) // => 1
fmt.Println(C) // => 2


列挙

const 宣言内で、定数ジェネレータ iota を使うと 0からの列挙値(int型)を生成できる。

const (

_ = iota // blank identifier を使えば、0 を破棄できる
A
B
C
)
fmt.Println(A) // => 1
fmt.Println(B) // => 2
fmt.Println(C) // => 3

const宣言が現れると iota は0にリセットされる。また、定数式毎にiota値はインクリメントされる。

const (

A = iota + iota // 0 + 0 (同じ式に二回現れても、iotaの値は同じ)
B // 1 + 1
C // 2 + 2
)
fmt.Println(A) // => 0
fmt.Println(B) // => 2
fmt.Println(C) // => 4


基本型

bool

string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 の別名
rune // int32 の別名
// Unicode のコードポイントを表す
float32 float64
complex64 complex128


変数の型を取得

import "reflect"

fmt.Println(reflect.TypeOf("foo")) // => string

// 表示だけなら、書式 %T も可
fmt.Printf("%T\n", "foo") // => string


ゼロ値

初期値を設定しない変数はゼロ値で初期化されている


bool
false

整数型
0

浮動小数点型
0.0

string
""

ポインタ、関数、インタフェース、スライス、チャネル、マップ型
nil

構造体、配列
各要素がそのゼロ値


スコープ


  • パッケージ

  • ファイル

  • ブロック {}

  • if, for, switch

  • switch, select の case節


リテラル

1

070 // 8進数
0x10 // 16進数
1.0
true
false
"foo"
`bar\n` raw string ("bar\\n"と同じエスケープ文字の効果がない)
[]int{0,1,2}
map[string]int{"a": 1; "b": 2 }
nil // ポインタ、関数、インタフェース、スライス、チャネル、マップ型のゼロ値。スライスなら空の値(長さ0,容量 0)


キャスト

x := uint8(10)


配列、スライス

配列は、メモリが割り当てられた固定の領域

スライスは配列の部分参照

[n]T  // 型表記: 型Tの配列。長さn

[]T // 型表記: 型Tの配列を参照できるスライス

var p []int = []int{0,1,2,3} // intの配列を生成しそのスライスを定義

p[2] => 2 // 要素参照

* 配列の範囲外参照はruntime error
* 負の数での参照もruntime error (即値で書けばコンパイルエラー)

len(p) // => 4 // 配列の長さ
cap(p) // => 4 // 配列の容量(capacity)=長さ

// スライス

a := [4]int{0,1,2,3} // 配列を生成

a[lo: hi] // 配列またはスライスa の lo から、hi-1までのスライスを返す

以下はいずれも配列と同じ長さのスライスを返し、同じ内容を参照する
a[0:len(p)]
a[0:]
a[:len(p)]
a[:]

// 配列aのインデックスnから長さlenのスライス
a[n: n+len]

a := [4]int{1,2,3,4}

s := a[0:2]

fmt.Println(s) // [1 2]
fmt.Println(len(s)) // => 2
fmt.Println(cap(s)) // => 4
// fmt.Println(s[2]) // => lenを超えた参照はcap内でもruntime error

// スライスは元の配列と同じ位置を指している
s[0] = 10
fmt.Println(a) // [10 2 3 4]
fmt.Println(s) // [10 2]

// aに別の配列を代入するとスライスも変わる(配列への代入は全要素のコピー、スライスへの代入は参照先の変更)
a = [4]int{2,4,6,8}
fmt.Println(a) // => [2 4 6 8]
fmt.Println(s) // => [2 4]

上記例で、配列型[4]int(2箇所)をスライス型[]intにすると最後の結果が変わる([10 2]のまま)。ほとんどの場面では上記のような明示的な配列型[4]intは使わずにスライス型[]intの変数を利用するのがよい。

なお、[...]int{1,2,3,4}と"..."を使えば、長さを明記せずに配列型を書くことができる。(配列の長さを記入したのと同じ)

v := [...]int{1,2,3,4}

fmt.Printf("%T\n", v) // => [4]int

a := [...]int{1,2,3,4}

s := a[0:3]
fmt.Println(a) // => [1 2 3 4]
fmt.Println(s) // => [1 2 3]

// スライスに要素を追加。cap内なら既存の要素が書き換わる
s = append(s, 5)
fmt.Println(a) // => [1 2 3 5]
fmt.Println(s) // => [1 2 3 5]

// スライスに要素を追加。capを超えると新たな配列が生成される
s = append(s, 6)
fmt.Println(a) // => [1 2 3 5]
fmt.Println(s) // => [1 2 3 5 6]

// 生成

s := make([]int, 5) // 中身は0で初期化
s := make([]int, 5, 100) capacity 指定(長さ100の配列を生成し、len=5、cap=100のスライスを返す)
cap(s) // => 100

// 拡張/縮小(re-slicing)

スライス演算で、cap以上の拡張はできない
s = s[:cap(b)]


マップ(map)

他の言語で連想配列やハッシュ、辞書のこと

map[K]T   // 型表記: 型Tを値、型Kをキーとするマップ

// 補足: map[K] は、T型であると読める。go では、型表記がC言語のように規則的であり、かつ逆向きのためC言語ほど複雑にならない。

// 生成

foo := make(map[キーの型]値の型)

foo := map[string]uint32 {
"a": 1,
"b": 2, // 最後の要素に余分なカンマを書ける
}

// 参照

foo["a"] // キーがなければ値の型のゼロを返す

// キーの有無確認は、2番目の戻り値で判断する
v, ok := foo["c"]
if ok == true {
// あり
}

// 置き換え

bar["b"] = 2

// 削除

delete(foo, "a")


演算子

// 論理演算子

"||"
"&&"
"!"

// 比較演算子
"==" | "!=" | "<" | "<=" | ">" | ">="

// 算術演算子
"+" | "-" | "*" | "/" | "%"

// ビット演算子
"|" | "^" | "&" | "&^"
"<<" | ">>"

// アドレス演算子
"&" | "*"

// 受信演算子(channelから値を取得)
"<-"


制御構造


分岐

if 文

if i > 0 {

} else if i < 0 {

} else {

}

// else の前で改行するとエラー(以下はNG)
if i > 0 {
}
else {
}

// 変数定義付(スコープはifの条件式とブロックの中のみ)
if i := 0; i > 0 {

} else if i < 0 {

}

switch 文

case 節にbreak は不要。逆に次のcaseを実行するには fallthrough を書く。

複数のcaseをまとめるには、,で区切る。

case 節の値は任意の式が使える。case 式は上から順に評価される。

switch i := 0; i {

case 0:
case 1, 3:
fallthrough
default:
}

switch の式を省略するとtrueの意味になる。

switch {
case i == 0:
case i == 1 || i == 3:
fallthrough
default:
}

type switch

動的型に対する型による分岐で利用する。

type switch 文では fallthrough は使用できない。

// 型 interface{} は、任意の型を受け付ける(後述のinterfaceを参照)

func foo(v interface{}) {
switch t := v.(type) { // ".(type)" は常にこう書く
case nil:
fmt.Printf("nil: %#v %T\n", t, t)
case bool:
fmt.Printf("bool: %#v %T\n", t, t)
case int:
fmt.Printf("int: %#v %T\n", t, t)
default:
fmt.Printf("default: %#v %T\n", t, t)
}
}

foo(nil) // => nil: <nil> <nil>
foo(1) // => int: 1 int
foo("xx") // => default: "xx" string


ループ

// for  ループ

for i:= 0; i < 10; i++ {

}

// foreach ループ
// range キーワードを使う(range はforでのみ使用できる構文)

for i, v := range []int{1,2,3,4} {
fmt.Printf("%d, %d\n", i, v)
}

// while ループ
i := 0
for ; i < 10; {

i++
}

i := 0
for i < 10 {

i++
}

// 無限ループ

for {

}


関数

func(V)T   // 型表記: 型Tの値を返す、型Vを引数に取る関数

func 関数名(引数) 戻り値の型 {

return x
}

複数の戻り値を返すことができる

func 関数名(引数) (string, string) {

return x, y
}

戻り型に変数名を書くと return の引数は不要になる

(return に引数を書いてもよい。その値が返る)

func 関数名(引数) (x, y string) {

return
}

以下のようには書けない(変数の定義が重複しているためエラーになる)

func 関数名(x, y string) (x, y string) {

return
}


クロージャ

z := 2

foo := func(x, y int) int {
return x*y*z
}

for i, v := range []int{1,2,3,4} {
fmt.Printf("%d * %d * %d = %d\n", i, v, z, foo(i, v))
}

func foo() func()int {

z := 1
return func() int {
z++
return z
}
}

func main() {
bar := foo()
fmt.Println(bar()) // => 2
fmt.Println(bar()) // => 3
}


構造体

struct {T}   // 型表記: 型Tの値をメンバーに持つ構造体

type 構造体名 struct {

X, Y int
}

// 定義(struct literal)

v := 構造体名{1, 2}
v := 構造体名{x: 1} // y 0
v := 構造体名{} // x, y => 0

// メンバ参照

v.X


ポインタ

ポインタは、C言語のそれと同じ変数という入れ物の位置を指す型。オブジェクトに対する参照ではない。アロー演算子(->)はない。

p := &v

p.X = 2
fmt.Println(v.X) # => 2

関数の引数はポインタ渡しでなければ、値のコピーが渡るので呼び出し元に影響しない

type T struct {

X int
}

func foo(v T) {
v.X = 2
}

func main() {
v := T{1}
foo(v)
fmt.Println(v) // => {1}
}

定義を

foo(v *T)

呼び出しを

foo(&v)

とすれば、{2}が返る


new (メモリ割り当て)

ゼロ初期化した(zeroed ) T の値をメモリに割り当て、そのポインタを返す

var t *T = new(T)

または、
t := new(T) # t は、Tのポインタ型

make() は特殊なメモリ割り当て関数で、参照型であるスライス、マップ、チャネル専用の割り当てに利用する。(内部のデータ構造を割り当てその参照であるスライス、マップ、チャネルを返す)


メソッド(method)

// 定義

func (p *T)foo() int {
return p.X
}

// 呼び出し(vは、T型)
v.foo()

パッケージ外の型や基本型に対して、メソッドを定義できない

基本型をもとに作った型には定義できる

type MYINT int

func (p *MYINT)foo() int {
return p * 2
}

v := MYINT(1)
v.foo()


interface

メソッド群を規定した型

interface {

Foo() int
Bar() int
}

同じメソッドを持つ型はそのinterfaceを実装していると見なされる

package main

import "fmt"

type I interface {
Foo() int
}

type T struct {
X int
}

func (p T) Foo() int { // (p *T)ではだめ
return p.X
}

func main() {
var v I
v = T{1} // interface I と同じメソッドFoo()を持つので代入可能
fmt.Println(v.Foo())
}

メソッドのレシーバ型がポインタなら、インタフェース型の変数にはアドレスを代入する

func (p *T) Foo() int { // (p *T)

return p.X
}

var v I = &T{1} // & でポインタを代入する

メソッドのレシーバ型が値型の場合は、どちらでもOK

func (p T) Foo() int { // (p *T)

return p.X
}

var v I = T{1} // 値も可能
var v I = &T{1} // ポインタも可能

すべての型は空のinterface interface {} を実装していると見なされる

var v interface{}

v = 1 // 何でも代入可能

var n int
n = v // 逆はNG

interface の型名を列挙することでそのinterfaceを参照できる

関節的でも参照をループすることはできない。

type I1 interface {

Foo() int
}

type I2 interface {
I1 // Foo() int と書いたのと同じ
Bar() int
}


concurrency

goroutine (go)

// goroutineを生成し関数foo()を並行に実行する

go foo(x, y, z)

// 無名の関数呼び出しを使えば、関数をあらかじめ作らずに並列処理を書くことができる
go func(x int) {
fmt.Println(x)
}(1)

channel (chan)

chan T  // 型表記: T型を送受信するチャネル

// 生成

ch := make(chan int)
ch := make(chan int, 100) // バッファ数指定

// 送信
ch <- v //バッファがいっぱいならブロックする
// 受信
v := <- ch // データがなければブロックする

func foo(c chan int) { c <- 1 }

func bar(c chan int) { c <- 2 }

c := make(chan int)

go foo(c)
go bar(c)
x, y := <- c, <- c // x, y は、1, 2 または、2, 1 が返る

// chanは参照型のためポインタ渡しする必要はない

チャネルを閉じる

close(ch)

閉じるのは送り手側が行う

受け側は入力があるか(閉じられているかを)2番目の戻り値で判定する

v, ok := <-ch

if ok == true {
// ある
}

入力が閉じられるまでループ

for i := range ch {

}

例:

func foo(c chan int) {

for _, v := range []int{1,2,3} {
fmt.Println(v)
c <- v
}
close(c)
}

c := make(chan int)
go foo(c)
for i := range c {
fmt.Println(i * 10)
}
// 順番は保証されない
1
10
2
20
3
30

// 並列処理の完了待ち合わせを簡単に書くサンプル

package main
import "fmt"

func main() {
c := make(chan bool)

go func() {
// 何か長い処理
fmt.Println("goroutine")
c <- true // クロージャなので外側のchannelにアクセス可能
}()

// メインの処理

<- c // 待ち合わせ
fmt.Println("finish")
}


select 文

複数のchannelからの受信を同時に待つ。 先にデータが来たchannelのみ処理する。

select実行時に既に複数のデータが届いていたら、いずれかのcaseをランダムに処理する。

default が指定されていたらデータが届いていないときブロックせずにdefaultの処理を実行する

select {

case v <- ch1:
fmt.Println(v)
case v <- ch2:
fmt.Println(v)
default:
fmt.Println("nothing")
}


type 文

type は型に別名をつける文。別名の型と元の型は区別される

type X struct {

X int
}

func foo() struct{int} {
x := struct {int}{1}
y := X{1}

// return y はng、 return x はok
return y // cannot use y (type X) as type struct { int } in return argument
}


その他


時刻

現在時刻

import "time"

time.Now()


Sleep

import "time"

time.Sleep(ns) // ns は、ナノ秒
time.Sleep(1*time.Second) // 1秒スリープ


文字列操作(Split, Join)

import . "strings"

s := []string{"abc", "def", "ghi"}

v := Join(s, ",")
fmt.Printf("%#v\n", v) // => "abc,def,ghi"

x := Split(v, ",")
fmt.Printf("%#v\n", x) // => []string{"abc", "def", "ghi"}

x = SplitN(v, ",", 2)
fmt.Printf("%#v\n", x) // => []string{"abc", "def,ghi"}