技術めも

Go 言語

基本

// 実行
$ go run test.go

// コンパイル
$ go build test.go

// フォーマット(コード規約)
$ go fmt test.go

// ドキュメント(パッケージ)
$ godoc fmt

// ブラウザでドキュメント
$ godoc -http=":3000"
 → http://localhost:3000
// プログラム開始位置
package main
func main() {
    // first start
}
import (
    f "fmt"
    _ "github.com/wdpress/gosample"
    . "strings"
)

組み込み型

uint8       符号なし  8-ビット 整数 (0 to 255)
uint16      符号なし 16-ビット 整数 (0 to 65535)
uint32      符号なし 32-ビット 整数 (0 to 4294967295)
uint64      符号なし 64-ビット 整数 (0 to 18446744073709551615)

int8        符号あり  8-ビット 整数 (-128 to 127)
int16       符号あり 16-ビット 整数 (-32768 to 32767)
int32       符号あり 32-ビット 整数 (-2147483648 to 2147483647)
int64       符号あり 64-ビット 整数 (-9223372036854775808 to 9223372036854775807)

float32     IEEE-754 32-ビット 浮動小数値
float64     IEEE-754 64-ビット 浮動小数値

complex64   float32の実数部と虚数部を持つ複素数
complex128  float64の実数部と虚数部を持つ複素数

byte        uint8の別名(エイリアス)
rune        int32の別名(エイリアス)

uint        32 または 64 ビット
int         uintと同じサイズ
uintptr     ポインタの値をそのまま格納するのに充分な大きさの符号なし整数
error     エラーを表すインターフェイス

変数

var message string = "hello world"

func main() {
    fmt.Println(message)
}

宣言

var foo, bar, buz string = "foo", "bar", "buz"

var (
   a string = "aaa"
   b = "bbb"
   c = "ccc"
)
func main() {
    // 関数内部での宣言と初期化
    var message1 string = "hello world"
    message2 := "hello world"

    // 定数
    const Hello string = "hello !!!"
}

省略形式の時は少なくともひとつ新しい変数であれば、他は再宣言ではなく単なる代入として扱える

var i int
i, j := 10, 20
var err error
i, err := hoge()

ゼロ値

// 暗黙の初期化
整数型    0
浮動小数点型    0.0
bool    false
string    ""
配列    各要素がゼロ値の配列
構造体   各フィールドがゼロ値の配列
その他の型    nil

if

// 丸括弧なし  (  )
// 波括弧省略不可 {  }
// 参考演算子なし

if a > b {
    fmt.Println("A")
} else if a == b {
    fmt.Println("B")
} else {
    fmt.Println("C")
}

for

for i := 0; i < 10; i++ {
    fmt.Println(i)
}

n := 0;
for n < 10 {
    fmt.Printf("N = %d", n)
    n++
}

for {
    doSameing()
}

switch

次の case に処理は移らない
次に移す場合は fallthrough を記載

n := 10
swith n {
case 15:
    fmt.Println("Fizz")
case 5, 10:
    fmt.Println("Buzz")
    fallthrough
default:
    fmt.Println(n)
}

式も記載可能

n := 10
swith n {
case n%15 == 0:
    fmt.Println("FizzBuzz")
case n%5 == 0:
    fmt.Println("Buzz")
default:
    fmt.Println(n)
}

関数

複数の値を返せる

func hello() {
    fmt.Println("hello")
}

// func sum(i int, j int) と同様
func sum(i, j int) {
    fmt.Println(i + j)
}

func sum(i, j int) int {
    return i + j
}

func swap(i, j int) (int, int) {
    return j, i
}
func main() {
    x, y = swap(1, 2)
    x, _ = swap(3, 4)  // 第2戻り値を無視
    swap(5, 6) // OK
}

エラーを返す関数

file, err := os.Open("test.txt")
if err != nil {
    // エラー処理
}

// 自作のエラー
func dev(i, j int) (int, error) {
    if j == 0 {
        return 0, error.New("Divied by Zero")   // 要 import "errors"
    }
    return i / j, nil
}

名前付き戻り値

すべて return のみで書ける

func dev(i, j int) (result int, err error) {
    if j == 0 {
        err = error.New("Divied by Zero")
        return  // 以下と同様 return 0, err
    }
    return  // 以下と同様 return i / j, nil
}

関数リテラル(無名関数)

main() {
    func(i, j int) {
        fmt.Println(i + j)
    }(2, 4)
}

配列

配列は固定長(可変長配列はスライスを利用)
[6]string という名前の型
[4]string と [2]string は別の型
関数に配列を渡す場合は値渡し

var arr [4]string

arr[0] = "a"

// 初期値設定
arr := [4]string{"a", "b", "c", "d"}

// サイズを暗黙指定
arr := [...]string{"a", "b", "c", "d"}

スライス(可変長配列)

シビアなメモリ管理を必要としないのであればこちらを使用

var s []string

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

append()

スライスの末尾に値を追加

var s []string
s = append(s, "a")
s = append(s, "b", "c")

s1 := []string{"a", "b"}
s2 := []string{"c", "d"}
s1 = append(s1, s2...)  // s2が展開され追加

range

配列やスライスの内容を先頭から順に取得
string, マップ, チャンネル にも使用可能

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

for i, s := range arr {
    fmt.Println(i, s) // 0 a ...
}

値の切り出し

string, 配列, スライス から値を部分的に取得

s := []int{0, 1, 2, 3, 4, 5}

s[2:4]         // [2 3]
s[0:len(s)]   // [0 1 2 3 4 5]
s[:3]           // [3 4 5]
s[:]             // [0 1 2 3 4 5]

可変長引数

★ fuc sum(nums []int) (result int) ではダメなのか?

fuc sum(nums ...int) (result int) {
    for _, n = range nums {
        rezult += n
    }
    return
}

sun(1, 2, 3, 4, 5)

マップ(連想配列)

// 宣言
var month map[int]string = map[int]string{}

month[1] = "January"
month[2] = "February"

// 宣言と初期化
month := map[int]string{
    1: "January",
    2: "February",
}

// 取り出し
jan := month[1]

// 存在確認
_, exist := month[1]
if exist {
    // データがあった場合
}

// データ削除
delete(month, 1)

// range で取り出し
for key, value := range month {
    fmt.Printf("%d %s\n", key, value)
}

ポインタ

参照渡しに使用
書き方はCと同様
ポインタ演算は使えない

func callByRef(i *int) {
    *i = 20
}

var i int = 10
callByRef(&i)

defer

指定した処理が関数を抜ける直前に必ず実行される仕組み
ファイルポインタのクローズ等に使用

file, err := os.Open("./error.go")
if err != nil {
    // エラー処理
}
// 関数を抜ける前に必ず実行される
defer file.Close()

パニック(try-catch)

パニックで発生したエラーはrecover()という組込み関数で取得
recover()をdeferの中に書くことでパニックで発生したエラーの処理を実施してから関数を抜ける

//  関数を抜ける前に必ず実行される無名関数
defer func() {
        err := recover()
        if err != nil {
            // runtime error: index out of range
            log.Fatal(err)
        }
}()

panic()

パニックは組込み関数panic()を用いて自分で発生

panic(errors.New("index out of range"))