Golangの基礎

参考

ドキュメント - The Go Programming Language

Golangの基礎としてはまず,tour of goを一通り見て理解します。

A Tour of Go

tour of go

Packages

goのプログラムの開始

mainパッケージからスタート

パッケージ名

importの最後のパス

※これによって関数の所属するパッケージが明確になります。

Exported names

最初の文字が大文字で始まる名前は外部のパッケージから参照できます

※exportされます

Functions

定義

func 関数名 (変数 *型) (引数1 型1, 引数2 型2) (返り値1の型, 返り値2の型) {
    return 返り値1, 返り値2
}

型が後置の理由




https://blog.golang.org/gos-declaration-syntax

Variables

関数の外

var i = 1
var str = "hoge"

関数の中

i := 1
str := "hoge"

※変数の型は右側の変数から型推論されます

Basic types

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

rune // int32 の別名
     // Unicode のコードポイントを表す

float32 float64
complex64 complex128

※bool, string, int, float64がよく使います
※intは64-bitまでなので、足りない場合はconstを使う

Constants

定数にできるもの

文字(character)
文字列(string)
boolean
数値(numeric)

定義

const (
  hoge = "hoge"
)

※:=は使えない


for

定義

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

※iのスコープはfor内

無限ループ

for {
}

if

定義

if hoge := Hoge.hoge(x); hoge > 0 {
    return hoge
}

hogeはif文のスコープ内or else内でのみ使用可能です
※基本elseは使用しないとは思いますが

switch

定義

func main() {
    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の最後で自動的にbreakされます
※breakさせたくない場合はfallthrough
※基本的にはswitchを使っている時点で複数のものがひとつの関数にきています ※switchを使うよりも先にメソッドを分けて、リファクタしてください

定義2

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

※if-elseが大量にある場合は以下のように書き直せます
※swtich true {}と同じです ※基本的にはif-elseを使っている時点で複数のものがひとつの関数にきています ※switch {}を使うよりも先にメソッドを分けて、リファクタしてください

defer

定義

func main() {
    defer fmt.Println("world2")
    defer fmt.Println("world")

    fmt.Println("hello")
}

※呼び出し元の関数がreturnするまで実行されません
※deferはLIFOのスタックに格納されます

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


Pointers

値取得

value := *pointer

ポインター取得

pointer := &value

ポインターに値設定

*pointer := value

Arrays

定義

var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)

※配列のサイズを変えることはできません

Slices

定義

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

var s []int = primes[1:4]
fmt.Println(s) // [3 5 7]

※スライスは可変長(柔軟な配列)
※配列よりも使われます

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

names := [4]string{
  "John",
  "Paul",
  "George",
  "Ringo",
}
a := names[0:2]
b := names[1:3]
b[0] = "XXX"
fmt.Println(a, b) // [John XXX] [XXX George]
fmt.Println(names) // [John XXX George Ringo]

※スライスの要素を変更すると元となる配列の要素が変更されます
※同じ元となる配列を共有している他のスライスは変更が反映されます

Slice literals

[]bool{true, true, false}

※[3]bool{true, true, false}の配列を作成して、それを参照するスライスを返す

Slice defaults

var a [10]int

a[0:10]
a[:10] // a[0:10] 
a[0:] a[0:10]
a[:] a[0:10]

Slice length and capacity

定義

len(s) // 要素の数
cap(s) // 元となる配列の要素数

s := []int{2, 3, 5, 7, 11, 13}
s = s[:0] // len=0 cap=6 []
s = s[:4] // len=4 cap=6 [2 3 5 7]
s = s[2:] // len=2 cap=4 [5 7]

Nil slices

var s []int // s == nil
fmt.Println(s, len(s), cap(s))  // [] 0 0

※要素の内スライス == nil

Creating a slice with make

hoge := make([]type, len, cap)
a := make([]int, 5) // len=5 cap=5 [0 0 0 0 0]
b := make([]int, 0, 5) // len=0 cap=5 []

※動的サイズの配列を作成

Slices of slices

// [[_ _ _] [_ _ _] [_ _ _]]
board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
}

※board[0]は[ _ ]

https://blog.golang.org/go-slices-usage-and-internals

append

var s []int
s = append(s, 1)
s = append(s, 2, 3, 4)

Range

インデックスを使う場合

for i, v := range slice {

}

インデックスを使わない場合

for _, v := range slice {

}

Maps

m = make(map[キーの型]バリューの型)

Map literals

type Vertex struct {
    Lat, Long float64
}

// map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68433, -74.39967,
    },
    "Google": Vertex{
        37.42202, -122.08408,
    },
}

Map literals continued

type Vertex struct {
    Lat, Long float64
}

// map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

※バリューの型はVertexなので、Bell Labsの要素は指定しなくてもVertexです

Mutating Maps

m[key] = elem
elem = m[key]
delete(m, key)
elem, ok = m[key]

Function values

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12))

    fmt.Println(compute(hypot))
    fmt.Println(compute(math.Pow))
}

rubyでいうlambdaのようなものです

Function closures

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

func main() {
    pos, neg := adder(), adder()
    for i := 0; i < 10; i++ {
        fmt.Println(
            i,
            pos(i),
            neg(-2*i),
        )
    }
}

0 0 0
1 1 -2
2 3 -6
3 6 -12
4 10 -20
5 15 -30
6 21 -42
7 28 -56
8 36 -72
9 45 -90

グローバル変数を使わなくてよくなります
※あまり使わない方がよいでしょう


TypeMethods

func (structValue Struct) method(arg1 argType) returnType {

}

Variable receivers

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

rubyでいうクラスの再オープンのようにメソッド追加できますが、型はオリジナルのものになります

Pointer receivers

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4}
    v.Scale(10) // {30 40}
    fmt.Println(v.Abs())}

※レシーバ自身を更新することが多いため、変数レシーバよりもポインタレシーバの方が一般的 ※変数でも、ポインターでもレシーバにできる
※v.Scale(10)は(&v).Scale(10)と同じ
※メソッドがレシーバが指す先の変数を変更するため,メソッドの呼び出し毎に変数のコピーを防げる ※golangの利点です
※メソッドはすべてポインタレシーバにすべき

Interfaces

type Abser interface {
    Abs() float64
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}
    a = v // Vertex does not implement Abser (Abs method has pointer receiver)
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

※メソッドのシグニチャの集まり

Interfaces are implemented implicitly

type I interface {
    M()
}

type T struct {
    S string
}

// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
    fmt.Println(t.S)
}

func main() {
    var i I = T{"hello"}
    i.M()
}

Interface

values

(value, type)

※空の場合は(, )

method

type T struct {
    S string
}

func main() {
    var i I

    i = &T{"Hello"}
    describe(i) // (&{Hello}, *main.T)
        i.M() // TのM()が実行される
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}

※基底クラスのメソッドが呼び出されます

values with nil underlying values

func (t *T) M() {
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

nilがレシーバのif文を入れるのが一般的 ※if文がないとinvalid memory address or nil pointer dereference(間接参照)

Type assertions

インターフェイスは型を必ず持っている場合

値 := インターフェイス.(型)

インターフェイスが型を持っていない場合
→panic: interface conversion: interface {} is XXXXXX, not 型

インターフェイスは型を持っているか確認する場合

値, ok := インターフェイス.(型)

Type switches

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

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

※基本的にswitchを使う時点でおかしいと思ってください
※複数のものを同じメソッドに流すのではなく、メソッドを分けてください

Stringers

type Stringer interface {
    String() string
}
type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z)
}

※Printlnの出力をカスタマイズすることができます
※Personのみなど、限定的に振る舞いを変えることができるます

Errors

出力時はerrorインターフェイスのErrorが実行される

type error interface {
    Error() string
}

nil以外はエラー

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}

Readers

func (T) Read(b []byte) (n int, err error)

func main() {
    r := strings.NewReader("Hello, Reader!")

    b := make([]byte, 8)
    for {
        n, err := r.Read(b)
        fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
        fmt.Printf("b[:n] = %q\n", b[:n])
        if err == io.EOF {
            break
        }
    }
}

※rを8バイト毎に読み込んでいる
※nはbに入れられたサイズ
※bにはバイトスライスが入る


Goroutines

go f(x, y, z)

※同じアドレス空間で実行されるため、共有メモリへのアクセスは必ず同期する必要があります

Channels

ch := make(chan int)
ch <- v    // v をチャネル ch へ送信する
v := <-ch  // ch から受信した変数を v へ割り当てる

x, y := <-ch, <-ch

※データは、矢印の方向に流れます

Buffered Channels

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    // v := <- ch
    ch <- 3 // fatal error: all goroutines are asleep - deadlock!
    // fmt.Println(v)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

Range and Close

close(ch)
v, ok := <-ch

※受信する値がない && チャネルが閉じているとokはfalseになる
※closeしたチャネルへ送信するとpanic: send on closed channel
※closeする必要はありません
※closeするのは、これ以上値が来ないことを受け手が知る必要があるときにだけです

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}

※cap©はcチャネルの容量つまり10です。
※close©しないとfatal error: all goroutines are asleep - deadlock!になります
※close©は処理が終わったら、rangeループを終了するということです

Select

select ステートメントは、goroutineを複数の通信操作で待たせます。

selectは複数あるcaseのいずれかが準備できるようになるまでブロックし、準備ができたcaseを実行します。 複数のcaseの準備ができている場合、caseはランダムに選択されます。

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

※fibonacci内でselectしているため、cもしくはquitの準備を確認します
※cはすでに作成済みのチャネルのため、case <- xが実行されます
※goroutineのquit <- 0 でcase <-quitで値が取れるため、case <-quitが実行されます

Default Selection

ブロックせずに送受信するならdefaultのcase使う

select {
case i := <-c:
    // use i
default:
    // receiving from c would block
}

sync.Mutex

goroutineとコミュニケーションする場合はチャネル
goroutineとコミュニケーションする必要がない場合はsync.Mutex
goroutineが変数へのコンフリクトを避けるために変数へのアクセスできるようにしたい場合はsync.Mutex

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key]++
    c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    defer c.mux.Unlock()
    return c.v[key]
}

func main() {
    c := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go c.Inc("somekey")
    }

    time.Sleep(time.Second)
    fmt.Println(c.Value("somekey"))
}

やることは以下です

1. mux sync.Mutexの定義
2. c.mux.Lock()
3. defer c.mux.Unlock()

tour of goを一通り理解したら次はこの動画を見ます。

www.youtube.com

次に大まかな流れを確認します。

How to Write Go Code - The Go Programming Language

How to Write Go Code

Workspaces

src contains Go source files,
pkg contains package objects, and
bin contains executable commands.

The go tool builds source packages and installs the resulting binaries to the pkg and bin directories.

bin/
    hello                          # command executable
    outyet                         # command executable
pkg/
    linux_amd64/
        github.com/golang/example/
            stringutil.a           # package object
src/
    github.com/golang/example/
        .git/                      # Git repository metadata
    hello/
        hello.go               # command source
    outyet/
        main.go                # command source
        main_test.go           # test source
    stringutil/
        reverse.go             # package source
        reverse_test.go        # test source
    golang.org/x/image/
        .git/                      # Git repository metadata
    bmp/
        reader.go              # package source
        writer.go              # package source

The GOPATH environment variable

$HOME/go
echo "export PATH=$PATH:$(go env GOPATH)/bin" >> ~/.bash_profile
echo "export GOPATH=$(go env GOPATH)" >> ~/.bash_profile

Import paths

A package's import path corresponds to its location inside a workspace or in a remote repository

Your first program

$ mkdir -p $GOPATH/src/github.com/user/app
$ go install github.com/user/app
$ app

※$GOPATH/bin/appに実行ファイルが置かれます

Your first library

$ mkdir $GOPATH/src/github.com/user/stringutil
// Package stringutil contains utility functions for working with strings.
package stringutil

// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}
$ go build github.com/user/stringutil

※bin/stringutilは不要なのでgo build

package main

import (
    "fmt"

    "github.com/user/stringutil"
)

func main() {
    fmt.Printf(stringutil.Reverse("!oG ,olleH"))
}
$ go install github.com/user/app

※pkg/linux_amd64/github.com/user/stringutil.aが作成される

stringutil.a

future invocations of the go tool can find the package object  
to avoid recompiling the package unnecessarily

Package names

package name

※The first statement in a Go source file
※All files in a package must use the same name
※Executable commands must always use package main
※There is no requirement that package names be unique across all packages linked into a single binary, only that the import paths (their full file names) be unique.

Testing

$GOPATH/src/github.com/user/stringutil/reverse_test.go

package stringutil

import "testing"

func TestReverse(t *testing.T) {
    cases := []struct {
        in, want string
    }{
        {"Hello, world", "dlrow ,olleH"},
        {"Hello, 世界", "界世 ,olleH"},
        {"", ""},
    }
    for _, c := range cases {
        got := Reverse(c.in)
        if got != c.want {
            t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
        }
    }
}

必要なこと

  • ファイル名は_test.go
$GOPATH/src/github.com/user/stringutil/reverse_test.go
  • ファイル内
1. import "testing"
2. func TestXXXX(t *testing.T) {}
3. 失敗時にはt.Error or t.Errorf or t.Fail

ベンチマークのテストしたい場合は*testing.B

  • 実行
$ go test github.com/user/stringutil

Remote packages

$ go get github.com/golang/example/hello

※go get = fetch && build && install


言語仕様

The Go Programming Language Specification - The Go Programming Language


美しく成熟するコード

vimeo.com


Go Concurrency Patterns

Go Concurrency Patterns


Advanced Go Concurrency Patterns

Advanced Go Concurrency Patterns


Share Memory by Communicating

Share Memory By Communicating - The Go Programming Language


A simple programming environment

Go: a simple programming environment

vimeo.com


Writing Web Applications

Writing Web Applications - The Go Programming Language


First Class Functions in Go

First-Class Functions in Go - The Go Programming Language


Goのメモリモデル

Goのメモリモデル - The Go Programming Language

Golangのメリット

コンパイルが早い

型システムには階層がない

気軽に新しい型を作成できる

並列実行と通信を言語としてサポート

完全なガベージコレクションを実装

シンプル

動的型言語の効率、コンパイル言語の静的な型の安全性を併せ持つ

参考

FAQ - golang.jp

インストールや各種設定

Go(golang)のインストールとSublimeText3(GoSublimeの設定)の開発環境構築 - keiwt’s diary
これ通りやれば、保存時にgo build(コンパイル), go test(テスト), go vet(ソースの静的解析), go lint(構文チェック)が動きますので楽ちんです。
※go build .で実行ファイルはカレントディレクトリに置かれます

言語に備わっているもの

build       compile packages and dependencies
clean       remove object files
doc         show documentation for package or symbol
env         print Go environment information
fix         run go tool fix on packages
fmt         run gofmt on package sources
generate    generate Go files by processing source
get         download and install packages and dependencies
install     compile and install packages and dependencies
list        list packages
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         run go tool vet on packages

以下により、ソースが綺麗で、バグの少ない、速くて、メモリ消費量の少ないソースになります。

[https://golang.org/pkg/testing/:title]

* go vet  
主にコンパイルでは発見することのできない静的エラーを指摘してくれます。  
※デフォルトで-allになっています

Assembly declarations →Mismatches between assembly files and Go function declarations.

Useless assignments →Check for useless assignments.

Boolean conditions →Mistakes involving boolean operators.

Build tags →Badly formed or misplaced +build tags.

Invalid uses of cgo →Detect some violations of the cgo pointer passing rules.

Unkeyed composite literals →Composite struct literals that do not use the field-keyed syntax.

Copying locks →Locks that are erroneously passed by value.

→Tests, benchmarks and documentation examples)

→Mistakes involving tests including functions with incorrect names or signatures and example tests that document identifiers not in the package.

→Failure to call the cancelation function returned by context.WithCancel.

→The cancelation function returned by context.WithCancel, WithTimeout, and WithDeadline must be called or the new context will remain live until its parent context is cancelled. (The background context is never cancelled.)

Methods →Non-standard signatures for methods with familiar names, including: Format GobEncode GobDecode MarshalJSON MarshalXML Peek ReadByte ReadFrom ReadRune Scan Seek UnmarshalJSON UnreadByte UnreadRune WriteByte WriteTo

Nil function comparison →Comparisons between functions and nil.

Printf family →Suspicious calls to functions in the Printf family, including any functions with these names, disregarding case: Print Printf Println Fprint Fprintf Fprintln Sprint Sprintf Sprintln Error Errorf Fatal Fatalf Log Logf Panic Panicf Panicln

Struct tags →Incorrect uses of range loop variables in closures.

Shadowed variables →Variables that may have been unintentionally shadowed.

Shifts →Struct tags that do not follow the format understood by reflect.StructTag.Get. Well-known encoding struct tags (json, xml) used with unexported fields.

Unreachable code →Unreachable code.

Misuse of unsafe Pointers →Likely incorrect uses of unsafe.Pointer to convert integers to pointers. A conversion from uintptr to unsafe.Pointer is invalid if it implies that there is a uintptr-typed word in memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector.

Unused result of certain function calls →Calls to well-known functions and methods that return a value that is discarded. By default, this includes functions like fmt.Errorf and fmt.Sprintf and methods like String and Error. The flags -unusedfuncs and -unusedstringmethods control the set.

[https://golang.org/cmd/vet/:title]  

* go lint  
※Effective GO通りになっているかチェックしてくれます。    

Golint differs from gofmt. Gofmt reformats Go source code ,whereas golint prints out style mistakes.

golint is concerned with coding style.

Golint makes suggestions for many of the mechanically checkable items listed in Effective Go and the CodeReviewComments wiki page.

[https://github.com/golang/lint/blob/master/README.md:title]

[https://blog.golang.org/error-handling-and-go:title]  

[http://golang.jp/go_faq#exceptions]

* メソッドと演算子のオーバロードがない

メソッドを呼び出す際に、メソッドの型をいちいち調べしなくてよい方がシンプルです。 他の言語に接した経験から言えることは、同じ名前で、かつシグネチャが異なるメソッドの寄せ集めを持つことは、 ときに役に立ちますが、混乱を招くだけで充分に機能しません。

* implementsがない

Goの型がインタフェースを満たすには、そのインタフェースのメソッドを実装する以外、何も必要ありません。 この特性により、既存のコードを修正せずにインタフェースを規定・使用することを可能にします。 これは、関連性の分離とコードの再利用を促進する「ダック・タイピング」の一種で、 コード開発時のパターン構築を容易にします。 このインタフェースの仕組みは、Goの小回りと軽量さに大きく寄与しています。

[http://golang.jp/go_faq#methods_on_values_or_pointers]

* シングルクオート・ダブルクオート

基本的にダブルクオート。 シングルクオートを使うと missing ‘ syntax error: unexpected ambda, expecting comma or )

The Go language defines the word rune as an alias for the type int32, so programs can be clear when an integer value represents a code point.

Moreover, what you might think of as a character constant is called a rune constant in Go. The type and value of the expression

‘⌘’ is rune with integer value 0x2318.

※シングルクオートだとalias for the type int32になります。  
[https://blog.golang.org/strings:title]