プログラミング言語Goを読みながらメモ(第七章)

[プログラミング言語 Go]( https://www.amazon.co.jp/dp/4621300253 ) を読みながらメモ。

第一章 : https://qiita.com/Nabetani/items/077c6b4d3d1ce0a2c3fd
第二章 : https://qiita.com/Nabetani/items/d053304698dfa3601116
第三章 : https://qiita.com/Nabetani/items/2fd9c372fcd8383955a5
第四章 : https://qiita.com/Nabetani/items/59bfd00dc3323883a07f
第五章 : https://qiita.com/Nabetani/items/4b785f1c9b0b26d48475
第六章 : https://qiita.com/Nabetani/items/1c100394a65af6506187

で

インターフェース。

静的型付け＋ダックタイピング という感じで面白い。

# 代入の方チェック

```go:go
package main

import "fmt"

type Foo struct{}

func (f Foo) Bar() {}
func (f Foo) Baz() {}

type Bar interface {
	Bar()
}

type BarBaz interface {
	Bar
	Baz()
}

func main() {
	foo := Foo{}
	var bar Bar = foo        // okay
	var barbaz1 BarBaz = foo // okay
	var barbaz2 BarBaz = bar // cannot use bar (type Bar) as type BarBaz in assignment:
	fmt.Println(foo, bar, barbaz1, barbaz2)
}
```

この通り、`bar` の正体が `foo` であっても、型チェックは静的に行われるのでエラーになる。
たぶん、無理矢理キャストする方法がこの後出てくると思うんだけど、これを書いている時点ではまだそこまで読み進めていない。

# interface の比較

比較できたり、比較しようとしたらパニックになったりする。ややこわい。

```go:go
package main

import "fmt"

func main() {
	var x interface{} = [3]int{1, 2, 3}
	fmt.Println(x == x) // okay, true.
	var y interface{} = []int{1, 2, 3}
	fmt.Println(y == y) // panic: runtime error: comparing uncomparable type []int
}
```

# nil っぽいものと nil であるもの

なんか罠らしい。

```go:go
package main

import (
	"bytes"
	"fmt"
	"io"
)

func main() {
	var buf *bytes.Buffer
	fmt.Println(buf, buf == nil) //=> <nil> true
	var out1 io.Writer
	fmt.Println(out1, out1 == nil) //=> <nil> true
	var out2 io.Writer = buf
	fmt.Println(out2, out2 == nil) //=> <nil> false
}
```

なるほど罠だ。

# sort

なんか使いにくい気がするこれ。

```go:go
package main

import (
	"fmt"
	"sort"
)

type StringSlice []string

func (s StringSlice) Len() int {
	return len(s)
}

func (s StringSlice) Less(i, j int) bool {
	// 二文字目で比較
	return s[i][1] < s[j][1]
}

func (s StringSlice) Swap(i, j int) {
	tmp := s[i]
	s[i] = s[j]
	s[j] = tmp
}

func main() {
	var strs StringSlice = []string{"oh", "be", "afine", "girl", "kiss", "me", "right", "now"}
	sort.Sort(strs)
	fmt.Println(strs) //=> [be me afine oh girl kiss right now]
}
```

「n 文字目で整列」のようなものを書く方法がわからない。

# 型アサーション

C/C++ でいうところのキャストは、「型アサーション」。

```go:go
package main

import "fmt"

type Foo struct{}

func (f Foo) Bar() {}
func (f Foo) Baz() {}

type Bar interface {
	Bar()
}

type BarBaz interface {
	Bar
	Baz()
}

func main() {
	foo := Foo{}
	var bar Bar = foo                 // okay
	var barbaz1 BarBaz = foo          // okay
	var barbaz2 BarBaz = bar.(BarBaz) // 「型アサーション」が必要
	fmt.Println(foo, bar, barbaz1, barbaz2)
}
```

型アサーションは、Cのキャストと異なり、失敗するとパニックになる。

```go:go
package main

import "fmt"

type Hoge struct{}
type Fuga struct{}

func (f Fuga) Bar() {}
func (h Hoge) Bar() {}
func (h Hoge) Baz() {}

type Bar interface {
	Bar()
}

type BarBaz interface {
	Bar
	Baz()
}

func main() {
	var hoge Bar = Hoge{} // okay
	var fuga Bar = Fuga{}
	var hoge2 BarBaz = hoge.(BarBaz) // okay, hoge can be BarBaz
	var fuga2 BarBaz = fuga.(BarBaz) // panic: interface conversion: main.Fuga is not main.BarBaz: missing method Baz
	fmt.Println(hoge, fuga, hoge2, fuga2)
}
```

しかし、第二引数がある場合にはパニックにならない。

```go:go
package main

import "fmt"

type Hoge struct{}
type Fuga struct{}

func (f Fuga) Bar() {}
func (h Hoge) Bar() {}
func (h Hoge) Baz() {}

type Bar interface {
	Bar()
}

type BarBaz interface {
	Bar
	Baz()
}

func main() {
	var hoge Bar = Hoge{} // okay
	var fuga Bar = Fuga{}
	hoge2, hoge2okay := hoge.(BarBaz) // okay, hoge can be BarBaz
	fuga2, fuga2okay := fuga.(BarBaz) // panic: interface conversion: main.Fuga is not main.BarBaz: missing method Baz
	fmt.Println(hoge, fuga, hoge2, hoge2okay, fuga2, fuga2okay) //=>{} {} {} true <nil> false
}
```

この、返戻値の数によって動作が変わるっていうのはどうなんだろうと思った。
まあ気持ちはわかるけれども。
ユーザーがそういう関数を書く方法はあるのだろうか？？

# 7章のおわり

もっと書きたいことがあるんだけど、この章はちゃんと設計していないとなかなか良さも悪さもわからない。
先に進もうと思う。