【Golang】Getting Started

言語の特徴

  • 簡潔性
    • 読解性
      • gofmtによる共通フォーマット
      • シンプルな文法
  • 生産性
  • 高速性

Hello World

package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

パッケージ

  • パッケージ外に公開する識別子は先頭を大文字にする
  • パッケージは複数ファイルから構成されても良い
  • init関数
    • パッケージの初期化用に定義できる関数
    • 引数も戻り値もない
    • ひとつのパッケージに複数定義できる(その場合の実行順序は定義順)
package main

import "fmt"

func init() {
    fmt.Println("init")
}
func main() {
    fmt.Println("main")
}

// 実行結果
// init
// main

import

  • 別名を指定してインポート
import f "fmt"

f.Println("test")
  • パッケージ名の省略
import . "fmt"

Println("test")

宣言

  • var:変数宣言
  • const:定数宣言
  • type:型宣言
  • func:関数宣言

データ型

  • bool
  • int8, int16, int32, int64
  • uint8, uint16, uint32, uint64
  • int(実装依存
  • uint(実装依存
  • uintptr(ポインタの値を格納)
  • float32
  • float64
  • runeUnicodeコードポイントを表す整数型。int32の別名。)
  • string

ポインタ

Cと使い方ほぼ同じ。ただしアドレスの加算減算はできない。

var n int = 1
var p *int = &n
*p = 5
fmt.Printf("n = %d\n", *p) // 5

定数

  • 型あり定数
const n int64 = 3
または
const n = int64(3)
  • 型なし定数
const n = 3
  • iota
    • 0オリジンの連番を採番
const (
    X = iota  // 0
    Y         // 1
    Z         // 2
)

配列

  • 配列を関数の引数に渡す際は参照渡しではなくコピーが渡される
var a1 [5]int   // すべてゼロ値で初期化
a2 := [5]int{}  // すべてゼロ値で初期化
a3 := [5]int{1, 2, 3, 4, 5}
a4 := [...]int{1, 2, 3}  // 要素数の省略 (結果として[3]int型となる)
a5 := [...]int{5: -1}    // インデックスを指定して値を設定({0,0,0,0,0,-1})

スライス

  • 可変長配列を表現
  • 容量を超えた要素数を追加すると、新たにメモリ領域を確保し、格納していた全データを新しいメモリ領域にコピーする(アドレスが変わる)
  • 事前に容量を大きく確保しておけば、新たなメモリ確保およびコピーが無くなる分の高速化が図れるが、不要にメモリを確保することにもなるので調整次第
var sli []int  // スライスの宣言
sli = make([]int, 5, 10)  // 要素数5、容量10のスライスを生成
sli2 := []int{1, 2, 3, 4, 5}  // makeを使わないスライス生成リテラル

fmt.Println(sli)  // [0 0 0 0 0 0 0 0 0 0] 初期値はゼロ値

fmt.Println(len(sli))  // lenで要素数を取得
fmt.Println(cap(sli))  // capで容量を取得

// 要素の追加はappend
sli1 := []int{1,2,3}
sli2 := []int{5,6,7}

s := append(sli1, 4)  // [1,2,3,4]
s = append(s, sli2...)  // ...で展開してスライス自体を追加
fmt.Println(s)  // [1,2,3,4,5,6,7]
  • 配列とスライス
    • 配列やスライスをもとに、新たなスライスを生成できる
a := [5]int{1, 2, 3, 4, 5}
sli := a[2:4]  // [3, 4]
  • 文字列とスライス
    • 文字列からスライスを生成できる
    • 文字単位ではなくバイト単位となることに注意
s := "あいうえお"  // それぞれ3バイトのマルチバイト文字
sli := s[3:9]  // いう

マップ

var m map[string]int
m = make(map[string]int)
m["Taro"] = 18
m["Hanako"] = 24
fmt.Println(m)

m1 := map[string]int{
    "Taro": 18,
    "Hanako": 24,
}

delete(m1, "Hanako")  // 要素の削除

構造体

type Person struct {
    Name string
    Age int
}
var Taro Person

Taro.Name = "Taro"
Taro.Age = 18

hanako := Person{"Hanako, 24}  // 構造体リテラル
Jiro := Person{Name: "Jiro", Age: 32}

関数

func funcA(x, y int) (string, error) {
    // 処理
    return "success", nil
}
  • 無名関数
f := func(a, b int) int { return a + b }
fmt.Println(f(3, 5))
func counter() func() int {
    var num int
    return func() int {
        num = num + 1
        return num
    }
}

func main() {
    c1 := counter();
    
    fmt.Println(c1())  // 1
    fmt.Println(c1())  // 2
    fmt.Println(c1())  // 3
    
    c2 := counter();
    
    fmt.Println(c2())  // 1
    fmt.Println(c2())  // 2
    fmt.Println(c2())  // 3
}

defer

  • 関数の終了時に実行する式を登録できる
  • 複数登録時は、最後に登録された式から実行される
func main() {
    defer fmt.Println("AAA")
    defer fmt.Println("BBB")
    defer fmt.Println("CCC")
    fmt.Println("DDD")
}

// 出力結果
// DDD
// CCC
// BBB
// AAA

panic

func panic(v interface{})
  • 即座にランタイムエラーを発生させる
  • 引数はエラーメッセージなどに使用できる
  • panicまでに登録されたdeferは実行される

recover

  • panicから回復することができる
  • deferと組み合わせて使われる
  • recoverの戻り値はnil(panicが起きていない)or panicの引数
  • recoverを実行するとpanicから復帰する
func panicFunc() {
    defer func() {
        if x := recover(); x != nil {
            // panicが起きた
            fmt.Println("recover")
        }
    }()
    panic("error")
}
func main() {
    fmt.Println("start")
    panicFunc()
    fmt.Println("end")  // 実行される
}

// 出力結果
// start
// recover
// end

メソッド

  • 関数をレシーバつきで定義
  • レシーバ変数を更新する場合はレシーバをポインタ型にする
  • レシーバの型がゼロ値としてnilを持つ場合はレシーバにnilを取ることもできる
func (u *User) say() {
    if u == nil {
        fmt.Println("Invalid User")
    } else {
        fmt.Println(u.Name)
    }
}

func main() {
    u := User{"Taro"}
    u.say()
}

インタフェース

type Creature interface {
    Say(str string)
}
  • 空インタフェース interface{}
    • すべての値を代入できる
    • 演算はできない
var any interface{}
any = 10
any = true

ゴルーチン

  • スレッドより小さいGo独自の処理単位
  • 関数実行の前にgoを付与するだけでゴルーチンを生成できる
func say() {
    fmt.Println("Hello")
}

func main() {
    go say()
    fmt.Printf("CPU num : %d\n", runtime.NumCPU())
    fmt.Printf("Goroutine num : %d\n", runtime.NumGoroutine())  // 2
}

チャネル

テスト

  • パッケージ内の*_test.goファイルがテスト対象
  • テストの種類
    • Testで始まるテスト関数:ロジックのテスト
    • Benchmarkで始まるベンチマーク関数:性能のテスト
    • Exampleで始まるコード例関数:ドキュメントの提供
func isEven(a int) bool {
    return a%2 == 0
}

func TestIsEven(t *testing.T) {
    type args struct {
        a int
    }
    tests := []struct {
        name string
        args args
        want bool
    }{
        {"case 1", args{2}, true},
        {"case 2", args{3}, false},
        {"case 3", args{4}, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := isEven(tt.args.a); got != tt.want {
                t.Errorf("isEven() = %v, want %v", got, tt.want)
            }
        })
    }
}