Go

インストール(Ubuntu)

VERSION='go1.12.9'
mkdir -p $HOME/downloads
(cd $HOME/downloads &&
    curl -O https://dl.google.com/go/$VERSION.linux-amd64.tar.gz &&
    sudo tar -C /usr/local -xzf $VERSION.linux-amd64.tar.gz)

.bashrcに追記する。

export PATH=$PATH:/usr/local/go/bin

Ref: https://golang.org/doc/install#install

基本

全部ここに書いてある内容と同じ。 https://go-tour-jp.appspot.com/basics

typeで任意の型を定義できる。

type MyInt int

var i MyInt = 10

変数

変数定義

varはパッケージか関数で利用できる。

var a, b, c string

func main() {
    var hoge int
}
# 複数の変数ごとに初期化子を書く。型を省略できる
var a, b, c = true, 123, "hoge"


func main() {
    # 関数の中なら:=で短くかける
    hoge := 1
}

初期値

初期値なしで宣言した変数の初期値。

  • 数値型: 0
  • bool型: false
  • string型: ""

型変換

型(変数)で型変換。

i := 42
f := float64(a)
u := uint(f)

型推論

:=var =で型を指定しない場合は右側の変数で型推論される。

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

定数

constを使う。 文字、文字列、boolean、数値でのみ使える。 :=を使って宣言はできない。

const Hoge = 1

func main() {
    const Fuga = "abc"
}

浮動小数点の変数を初期化して宣言する

// どっちでもOK
z := 1.0
z := float64(1)

関数

2つ以上の引数が同じ型の場合は最後の型以外を省略できる

func add(a, b int) int {
    return a + b
}

複数の戻り値を返す

func swap(a, b string) (string, string) {
    return b, a
}

戻り値の変数に名前をつける(named return value)

戻り値の意味を説明するドキュメントとして使える。

func hoge() (a, b int) {
    a = "hoge"
    b = "fuga"
    return
}

可読性が下がるのでnaked returnは短い関数のときだけ使う。

関数を変数として使う

func(引数) 戻り値の型で関数を変数として宣言できる。

add := func(a, b int) int {
    return a + b
}

fmt.Println(add(1, 2)) // 3

関数を受け取る関数

引数の型をfuncにすればいい。

func compute(fn func(a, b int) int) int {
    return fn(2, 1)
}

func add(a, b int) int {
    return a + b
}

func minus(a, b) int {
    return a - b
}

func main() {
    fmt.Println(compute(add)) // 3
    fmt.Println(compute(minus)) // 1
}

クロージャ

関数の中で宣言した変数の状態は、各関数の変数ごとに保持される。

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
    pos, neg := adder(), adder()
    fmt.Println(pos(2), neg(-2)) // 2 -2
    fmt.Println(pos(2), neg(-2)) // 4 -4
    fmt.Println(pos(2), neg(-2)) // 6 -6
}

For

変数iforのスコープ内でのみ有効。

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

初期化と後処理は省略できる。

sum := 1
for ; sum < 1000; {
    sum += sum
}

初期化と後処理を省略するときのセミコロンは省略できる。するとwhile文と同じになる。

sum := 1
for sum < 1000 {
    sum += sum
}

すべて省略すると無限ループになる。

for {
}

If

基本的なif

if x < 0 {
}

条件と一緒に、if文のなかでのみ有効な変数を宣言できる。

if v := math.Pow(x, n); v < lim {
    return v
} else {
    // if文で宣言した変数はelseブロック内でも使える
    fmt.Println(v)
}

Switch

caseに自動的にbreakが挿入されるのでbreakを書かなくていい。 if文と同様に、switch文を書くときに変数の宣言ができる。 caseは定数、整数でなくてもいい。

import (
	"runtime"
)

fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("OS X.")
case "linux":
    fmt.Println("Linux.")
default:
    // freebsd, openbsd,
    // plan9, windows...
    fmt.Printf("%s.", os)
}

自動的にbreakが入るので、次のときi==0ならfは呼び出されない。

switch i {
    case 0:
    case f():
}

if-then-elseが長くなるときは条件なしのswitchを書くとシンプルになる。

t := time.Now()
switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")
}

Defer

deferへ渡した関数の引数はすぐに評価するが、関数自体の呼び出しは元の関数がreturnするまで実行されない。

defer fmt.Println("world")
fmt.Println("hello")
// hello world

複数回deferすると最後のdeferから呼ばれる。

defer fmt.Println("A")
defer fmt.Println("B")
defer fmt.Println("C")
// C, B, A

deferはリソースの開放に使える。

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

Ref: https://blog.golang.org/defer-panic-and-recover

ポインタ

T型の変数のポインタは*T型で、ゼロ値はnil

var p *int

&演算子で変数のポインタを取得できる。

hoge := 1
fmt.Println(&hoge) // 0x40c128

*演算子でポインタの指す値を取得できる。

hoge := 1
p := &hoge
fmt.Println(*p) // 1

構造体

複数のフィールドをまとめて構造体として宣言できる。

type Vertex struct {
	X int
	Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(v) // {1 2}

    // フィールドへはドット(.)でアクセスできる
    fmt.Println(v.X) // 1

    // フィールドは再代入できる
    v.X = 4
    fmt.Println(v.X) // 4

    // ポインタを通してフィールドにアクセスできる
    p := &v
    (*p).X = 1e9
    fmt.println(v) // {1000000000 2}
    // (*p).Xの代わりに直接p.Xでも書ける
    p.X = 10
    fmt.println(v) // {10 2}
}

構造体変数の宣言いろいろ。 v2 = Vertex{X: 1}のようにName:構文を使うとき、フィールド指定順序は関係ない。

type Vertex struct {
	X, Y int
}

var (
	v1 = Vertex{1, 2}  // Vertex型
	v2 = Vertex{X: 1}  // Vertex型。Yは初期値0
	v3 = Vertex{}      // Vertex型。X, Yともに初期値0
	p  = &Vertex{1, 2} // *Vertex型。
)

配列

[n]T型は配列を表す。

// 要素10個のint型配列
var a [10]int

配列の長さは型の一部分で、あとからサイズを変えることはできない。

スライス

可変長の配列。型[]Tで表す。

スライスの生成。配列と違って要素数を指定しないで作れる。

s := []int{2, 3, 5, 7, 11, 13}

スライスのゼロ値はnil

var s []int
fmt.Println(s, len(s), cap(s)) // [] 0 0
if s == nil {
    fmt.Println("nil!") // nil!
}

構造体のスライスも作れる。

s := []struct {
    a int
    b int
}{
    {0, 0},
    {0, 1},
    {1, 0},
    {1, 1},
}

a[low:high]で、配列alow番目からhigh - 1番目までの要素をもつスライスを作れる。

primes := [6]int{2, 3, 5, 7, 11, 13}

var s []int = primes[1:4] // 要素1~3を含むスライスを作る
fmt.Println(s)
// [3 5 7]

スライスするときに上限と下限を省略したときは、下限が0、上限はスライスの長さになる。

s := []string{"a", "b", "c", "d", "e"}

fmt.Println(s[0:5]) // [a b c d e]
fmt.Println(s[0:])  // [a b c d e]
fmt.Println(s[:5])  // [a b c d e]
fmt.Println(s[:])   // [a b c d e]

スライスは配列への参照のようなもので、スライスの要素を変更すると元の配列も変更される。

names := [3]string{
    "A",
    "B",
    "C",
    }
fmt.Println(names) // [A B C]

a := names[0:2]
a[0] = "XXX"

fmt.Println(a) // [XXX B]
fmt.Println(names) // [XXX B C]

動的サイズの配列を作るには、組み込み関数makeを使う。 make関数ゼロ化された配列を割り当て、その配列を指すスライスを返す。

a := make([]int, 5) // len(a)=5

// 3番めの引数にスライスの容量を指定できる
b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4

スライスのスライスを作る。

ss := [][]string {
    []string{"a", "b", "c"},
    []string{"d", "e", "f"},
}

ss[0][0] = "A"

スライスに要素を追加する

append関数を使う。append関数へは追加元のスライスと追加する要素を渡す。

var s []int

fmt.Println(s) // []

s = append(s, 1)
fmt.Println(s) // [1]

s = append(s, 2, 3, 4)
fmt.Println(s) // [1 2 3 4]

Ref: https://golang.org/pkg/builtin/#append

range: スライスの要素分だけ繰り返す

for文でrangeを使うと、スライスのインデックスと値を繰り返しごとに返す。

s := []string{"a", "b", "c"}

for i, v := range s {
    fmt.Printf("index=%d, value=%d\n", i, v)
}

Map

// map[キー型]バリュー型 で宣言する
var m map[string]int

// ゼロ値はnil
if m == nil {
    fmt.Println("nil.")
}
// ゼロ値はキーの追加もできない
m["aaa"] = 1 // error!

// make関数は初期化された使用可能なマップを返す
m = make(map[string]int)
m["hoge"] = 1
fmt.Println(m["hoge"])

// 変数の宣言と同時に初期化する
m := map[string]int {
    "aaa": 1,
    "bbb": 2,
    "ccc": 3,
}

マップの追加・更新

m[key] = elem

マップの要素の取得

elem = m[key]

マップの要素の削除

delete(m, key)

マップに指定した要素が存在するか確認

m[key]が返す2番めの戻り値で確認できる。 mkeyが存在しない場合、elemはmapの要素型のゼロ値になる。

elem, ok = m[key]

if ok {
    // 要素あり
} else {
    // 要素なし
}

メソッド

Kotlinの拡張関数のような感じ。 既存の型にメソッドを定義できる。

type Point struct {
    X, Y int
}

func (p Point) Move(x, y int) Point {
    return Point{ p.X + x, p.Y + y}
}

func main() {
    p := Point{10, 20}
    fmt.Println(p.Move(2, 3)) // {12, 23}
}

任意の型にメソッドを宣言する

typeとメソッドを組み合わせると、次のように実質同じ型(intどうし)なのにMyIntとして宣言した変数しかAdd関数を呼べない、といったコードが書ける。

type MyInt int

func (a MyInt) Add() MyInt {
    return a + 10
}

func main() {
    var i MyInt = 1
    fmt.Println(i.Add()) // 11

    var ii int = 1
    fmt.Println(ii.Add()) // error! int型にAdd関数はない
}

ポインタレシーバを使ってメソッド内でレシーバ変数を変更する

メソッド引数の型をポインタ(*)にすると、引数のフィールドをメソッド内で変更できるようになる。 変数レシーバの場合、変数のコピーが引数に渡されるため、引数のフィールドを変更できない。 ポインタレシーバを使う場合、基本的にレシーバのnilチェックをする。

type Point struct {
    X, Y int
}

// ポインタレシーバ
func (p *Point) Move(x, y int) {
    if p == nil {
        // nilチェック
        fmt.Println("<nil>")
        return
    }
    p.X = p.X + x
    p.Y = p.Y + y
}

func main() {
    p := Point{0, 0}
    p.Move(1, 2)
    fmt.Println(p) // {1 2}
}

インタフェース

インタフェース型はメソッドシグニチャの集まりで定義する。

type Hoge interface {
    Func() int
}

var a Hoge

空のインタフェース

interface{}で空のインタフェースを定義できる。 TypeScriptのany型みたいなもの。

func describe(i interface{}) {
    fmt.Println(i)
}

型アサーション

変数名.(型名)で型アサーションができる。 1番目の戻り値が型変換済みの変数、2番めの戻り値は型アサーション結果(true or false)が入る。 型アサーションに失敗した場合は1番目の戻り値にゼロ値が入る。 2番目の戻り値を省略して型アサーションに失敗するとpanicが起きる。

var i interface{} = "hello"

s := i.(string)      // s=hello
s, ok := i.(float64) // s=0, ok=false
s := i.(float64)     // panic

Tips

byte -> string変換

var b byte = 127
s := fmt.Sprintf("%d", b)
// s="127"

Switchで型判定

変数名.(type)を使う。

func do(i interface{}) {
    switch v := i.(type) {
        case int:
            fmt.Println("i is int")
        case string
            fmt.Println("i is string")
        default :
            fmt.Println("others...")
    }
}

文字列を単語(スペース)区切のスライスに変換する

strings.Fields()を使う。

import "strings"

s = strings.Fields("A man a plan a canal panama")
// s=[A man a plan a canal panama]

型名を表示する

%Tを使う。

v := 10
fmt.Printf("v is of type %T\n", v)

HelloWorld

main.goを書く。

package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}

go runで実行する。

go run main.go

GAE/GO でHelloWorld

ファイル構成。

app.yaml
hello.go

app.yamlを書く。

runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app
package main

import (
        "fmt"
        "net/http"

        "google.golang.org/appengine"
)

func main() {
        http.HandleFunc("/", handle)
        appengine.Main()
}

func handle(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, world!")
}

ローカル環境で動作確認するには次のコマンドを実行する。 curl localhost:8080/でつながる。 ソースを変更するとアプリが再コンパイルされて起動される。

dev_appserver.py app.yaml

GAEにデプロイするには次のコマンドを実行する。

gcloud app deploy

Ref: https://cloud.google.com/appengine/docs/standard/go/quickstart?hl=ja#test_the_application

FAQ

go getでエラー

~/goを消してもう一度go getする。