352
335

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

golang チートシート

Last updated at Posted at 2015-01-15

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

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"}
352
335
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
352
335

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?