Fire Engine

化学の修士号→消防士→ITエンジニア(2016年11月〜)

「A Tour of Go」でGoに再入門した

もうかれこれ4ヶ月くらいGoを書いているんだけど、最初に文法を体系的に学ばずにいきなり書き始めたので、「A Tour of Go」で復習がてら文法を学びました。
実際にやってみると、理解が曖昧だったり、知らなかったことも出てきたので、そういったものだけピックアップして備忘録的にまとめていきます。

deferへ渡した関数はスタックされる

呼び出し元の関数がreturnするとき、 deferへ渡した関数はLIFO(last-in-first-out)の順番で実行される。

package main

import "fmt"

func main() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")

    fmt.Println("return")
}

//=>
return
3
2
1

型推論

明示的な型を指定せずに変数を宣言する場合(:=var =のいずれか)、変数の型は右側の変数から型推論される。

package main

import "fmt"

func main() {
    i := 10
    var j = 3.14
    fmt.Printf("%T\n", i)
    fmt.Printf("%T\n", j)
}

//=>
int
float64

ポインタ(*と&について)

package main

import "fmt"

func main() {
    i := 10

    p := &i         // アドレス演算子「&」を使うと、任意の型からそのポインタ型を生成できる
    fmt.Println(*p) // 演算子*をポインタ型の変数の前に置くことで、ポインタ型が指し示すデータのデリファレンスができる
    *p = 20         // ポインタを通してiに値をセットする
    fmt.Println(i)  
}

//=>
10
20

スライスは配列への参照のようなもの

スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示している。
スライスの要素を変更すると、その元となる配列の対応する要素が変更される。

package main

import "fmt"

func main() {
    names := [3]string{
        "John",
        "Paul",
        "George",
    }
    fmt.Println(names)

    a := names[0:2]
    fmt.Println(a)
    a[0] = "Tsurubee"
    fmt.Println(names)
}

//=>
[John Paul George]
[John Paul]
[Tsurubee Paul George]

下は配列リテラル

[3]bool{true, true, false}

そして、これは上記と同様の配列を作成し、それを参照するスライスを作成する

[]bool{true, true, false}

スライスは長さ(length)と容量(capacity)をもつ

package main

import "fmt"

func main() {
    s := []int{1, 2, 3, 4}
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)

    s = s[:2]
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

//=>
len=4 cap=4 [1 2 3 4]
len=2 cap=4 [1 2]

mapに要素が入っているか確認

キーに対する要素が存在するかどうかは、2つの目の返値で確認する。
もし、keyがあれば、変数okはtrueとなり、存在しなければ、okはfalseとなる

package main

import "fmt"

func main() {
    m := make(map[string]int)
    m["Answer"] = 10

    v, ok := m["Answer"]
    fmt.Println(v, ok)
}

//=>
10 true

アサーション

インターフェースの値が特定の型を保持しているかどうかをテストするために、型アサーションは2つの値(基になる値とアサーションが成功したかどうかを報告するブール値)を返すことができる。

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)
}

//=>
hello true
0 false

型switch

型switchは通常のswitch文と似ているが、型switchのcaseは型(値ではない)を指定し、それらの値は指定されたインターフェースの値が保持する値の型と比較される。

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Println("Int type!")
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(10)
    do("hello")
    do(true)
}

//=>
Int type!
I don't know about type string!
I don't know about type bool!

selectで複数のチャネルを処理する

selectは、複数あるcaseのいずれかが準備できるようになるまでブロックし、準備ができた caseを実行する

package main

import "fmt"

func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch2 <- 1

    select {
        case <-ch1:
            fmt.Println("ch1!")
        case <-ch2:
            fmt.Println("ch2!")
        default:
            fmt.Println("default!")
    }
}

//=>
ch2!