A Tour of Goやってみた

Packages

  • phpのようにルートからパッケージまでの経路を記述するのではなく、親パッケージの名前を書くだけで良い
    • 親パッケージの名前が重複した場合どうなるのか?

Imports

  • a tour of goはfactored import statementを推奨している

Exported names

  • javascriptみたいなイメージを持った
    • 関数のexportの面で似ていると感じた
// javascript
export function add(a, b) {
    return a + b;
}

// golang
func Add(a int, b int) int {
    return a + b
}

Functions

Functions continued

  • 引数に同じ型を取る場合は、型名を省略することができる
    • add(a int, b int)add(a, b int)

Multiple results

  • 関数は複数の戻り値を返すことができます

Named return values

  • 戻り値となる変数に名前をつけることができる
  • return ステートメントに何も書かなくて良い
  • 長いメソッドで使用すると可読性が下がる
  • 短いの記述とは?

Variables with initializers

  • varで変数を宣言することができる
  • :=との違いは何?
    • 関数外で使用するようなグローバルな変数に対してはvarを使用する、関数内で宣言する変数に関してはShort variable declarationsを使用する

Short variable declarations

  • :=で暗黙的な型宣言ができる
  • 関数外では使用できない
  • 定数は宣言できない

Basic types

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

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

float32 float64

complex64 complex128

Zero values

  • varで宣言だけして値を代入していない変数
    • 数値型(int,floatなど): 0
    • bool:false
    • string:""

Constants

  • constで定義する

構文

For

  • 他の言語と変わらない
  • Goにはwhileは存在しないため、for文で実現する
  • 何も記述しないと無限ループを実現することができる
// while
count := 1
for count < 5 {
    fmt.Println(count)
    count++
}

// 無限ループ
for {}

if

  • 他の言語と同じ
  • 省略は以下のようになる(ifステートメントで宣言された変数はブロック内でのみ使用可能)
// 省略
if v := math.Pow(x, n); v < lim {
    return v
}

Exercise: Loops and Functions ???

Switch

  • if-elseの簡略化
  • caseの最後に必要なbreak文が自動で提供されているため、breakは不要
  • 条件なしで記述することができる(if-then-elseの簡略化)
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.")
    }
 
 
    if t.Hour() < 12 {
        fmt.Println("Good morning!")
    } else if t.Hour() < 17 {
        fmt.Println("Good afternoon.")    
    } else {
        fmt.Println("Good evening.")
    }

Defer

  • deferに渡した関数を呼び出し元の関数がreturnされるまで遅延させる
  • 途中で抜けようが抜けまいが必ずやって欲しい処理をあらかじめ書ける(https://qiita.com/ika_tarou/items/f9d61b7a352d946ed20c
  • deferへ渡した関数が複数ある場合はLIFOの順でスタックされる(最後に入れたものが最初に取り出される)

新しい型の定義

Pointers

  • ポインタは値のメモリアドレスを指します
  • &でポインタを参照する
  • *ポインタから値を取り出す
a := 42
b := &a

i := 42
p = &i

fmt.Println(*p) // ポインタpを通してiから値を読みだす
*p = 21         // ポインタpを通してiへ値を代入する

Structs

  • フィールドの集まり
  • ドットでアクセスできる
  • structのポインタに関しては*は不要
func main() {
    v := Vertex{1, 2}
    p := &v
    p.X = 1e9
    fmt.Println(v)
}

Arrays

  • 配列のサイズも宣言する必要ある
  • 固定長配列
  • 値渡し
  • スライスより使用度は低い?
arrayA := [4]int{1, 2, 3, 4}
fmt.Println(arrayA)

arrayB := arrayA
arrayB[1] = 3
fmt.Println(arrayB)

// [1 2 3 4]
// [1 3 3 4]

Slices

  • 配列型の上に構築された抽象化
  • 可変長配列
  • go言語組み込みのmake関数でもスライスを作成することができる
  • 配列の参照のため、値を変更すると参照元も変わる
  • スライスは配列へのポインタと要素数(length)とメモリ領域(capacity)をもつ
  • 容量オーバーしたら基本的に2倍の値を確保します
  • ゼロ値はnil
  • appendで要素を追加することができる
  • rangeで
  • 反復処理を行うことができる
// 配列
names := [2]string {"yamada", "sato"}

// スライス
names := []string {"yamada", "sato"}

// スライスの操作
name := []int{0, 1, 2, 3, 4, 5}
name = name[1:4] // {1, 2, 3} len = 3 cap = 
name = name[:2]  // {1, 2}    len = 2
name = name[1:]  // {2}       len = 1

a := make([]int, 5, 5) // {0, 0, 0, 0, 0}

// 要素の追加
a2 := append(a, 1, 2, 3, 4) // {0, 0, 0, 0, 0, 1, 2, 3, 4}

参考

Maps

  • 連想配列のようなもの
  • make(map[キーの型]値の型)
  • deleteで要素を削除可能
a := make(map[string]int)
a["Answer"] = 42

a, exists := a["Answer"] // キーが存在したらexistsはtrue
delete(a, "Answer")      // 要素の削除

Method And Interface

Methods

  • クラスは存在しないが型にメソッドを定義することができる
  • 型にメソッドを定義するときは同じパッケージにある必要がある

  • typeとstruct違い

Pointer receivers

  • 構造体の変数の値を変更したいときは、ポインタで定義する
  • メソッドがレシーバが指す先の変数を変更する
  • 変数のコピーは非効率
  • 一般的には、値レシーバ、または、ポインタレシーバのどちらかですべてのメソッドを与え、混在させるべきではありません。
type Squere struct {
    Height int
    Width  int
}

func (s *Squere) Scale(f int) {
    s.Height = s.Height * f
    s.Width = s.Width * f
}

s := Squere{3, 4}
s.Scale(10) // height: 30, width: 40

Interfaces

  • インターフェースを実装することを明示的に宣言する必要はないのでimplementsキーワードなどは不要
  • Go では nil をレシーバーとして呼び出されても適切に処理するメソッドを記述するのが一般的です
  • 空のインターフェースは、未知の型の値を扱うコードで使用されます(empty interfacePHPでいうmixed、JavaScriptでいうany
  • t, ok := i.(T)で特定の型を保持しているかテストできる
type I interface {
    M()
}

type T struct {
    S string
}

func main() {
    var i I
    var t *T

    i = t
}

// 型の保持の確認
var t interface{} = "hello"
s, ok := t.(string)  // "hello", true
f, ok := t.(float64) // panic

Errors

  • GoではエラーをErrorオブジェクトで表現する。その多言語では例外処理でExceptionなどが存在する
  • error値がnilかどうかでハンドリングを行う
func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

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

参考