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

プログラミング言語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
第七章 : https://qiita.com/Nabetani/items/6553ad253af77661e915
第八章 : https://qiita.com/Nabetani/items/3b2e3964159cc292fe00
第九章 : https://qiita.com/Nabetani/items/c94e51aa29926a56159e
で。

第十章と第十一章は、特にメモしなかったので省略。

そして。
第十二章は、楽しいリフレクション。

# 構造体の中身を調べる

```go:go
package main

import (
	"fmt"
	"reflect"
)

func fields(t reflect.Type) []interface{} {
	if t.Kind() != reflect.Struct {
		return []interface{}{"not struct"}
	}
	numf := t.NumField()
	var names = []interface{}{}
	for n := 0; n < numf; n++ {
		f := t.Field(n)
		if f.Type.Kind() == reflect.Struct {
			names = append(names, fields(f.Type))
		} else {
			names = append(names, fmt.Sprintf("%q %v\n", f.Name, f.Type))
		}

	}
	return names
}

func main() {
	v0 := struct {
		foo  bool
		bar  int
		baz  []string
		hoge struct {
			fuga int
			piyo [12]string
		}
	}{}
	fmt.Println(fields(reflect.TypeOf(v0)))
}
```

を実行すると

```text
["foo" bool
 "bar" int
 "baz" []string
 ["fuga" int
 "piyo" [12]string
]]
```

が出力される。
よしよし。

構造体じゃないものに対して `NumField` を呼んだり、Slice, Array, String でないものに対して `Index` を呼んだりするとパニックらしい。

# CanAddr() と CanSet()

```go:go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	fmt.Println(reflect.ValueOf(2).CanAddr()) // false
	x := 2
	fmt.Println(reflect.ValueOf(x).CanAddr())         // false
	fmt.Println(reflect.ValueOf(&x).CanAddr())        // false
	fmt.Println(reflect.ValueOf(&x).Elem().CanAddr()) // true
	y := []string{"foo", "bar", "baz"}
	fmt.Println(reflect.ValueOf(y[0]).CanAddr())       // false
	fmt.Println(reflect.ValueOf(y).Index(0).CanAddr()) // true
}
```

こんな具合。なるほど。

`CanAddr()` ならアドレスが取れる。

```go:go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	x := 2
	xptr := reflect.ValueOf(&x).Elem().Addr().Interface().(*int)
	fmt.Println(*xptr) //=> 2
	*xptr = 123
	fmt.Println(x) //=> 123
	y := []string{"foo", "bar", "baz"}
	yzero := reflect.ValueOf(y).Index(0).Addr().Interface().(*string)
	fmt.Println(*yzero) //=>foo
	*yzero = "FOO"
	fmt.Println(y) //=> [FOO bar baz]
}
```

アドレスが取れなくても、 `SetInt` などで設定できる：

```go:go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	x := 2
	xv := reflect.ValueOf(&x).Elem()
	xv.SetInt(123)
	fmt.Println(x) //=> 123
	y := []string{"foo", "bar", "baz"}
	y0v := reflect.ValueOf(y).Index(0)
	y0v.SetString("FOO")
	fmt.Println(y) //=> [FOO bar baz]
}
```

しかし、`CanAddr()` であっても、`CanSet()`  ではない場合は Set できない。

```go:go
package main

import (
	"fmt"
	"os"
	"reflect"
)

func main() {
	stdout := os.Stdout
	fmt.Printf("%T\n", stdout) //=> *os.File
	vs := reflect.ValueOf(stdout).Elem()
	file := vs.FieldByName("file")
	pfd := file.Elem().FieldByName("pfd")
	sysfd := pfd.FieldByName("Sysfd")
	fmt.Printf("Sysfd = %v\n", sysfd)                               //=> Sysfd = 1
	fmt.Printf("addr:%v set:%v\n", sysfd.CanAddr(), sysfd.CanSet()) //=> addr:true set:false

	fmt.Printf("Sysfd = %v\n", sysfd.Addr().Interface())
	// panic reflect.Value.Interface: cannot return value obtained from unexported field or method
}
```

Set もなにも、 Interface を取ろうとするだけで panic になる。

# 出来ないこと

Ruby と違って実行時に構造体を定義したりは出来ないっぽい。
当たり前のような、そうでもないような。

まあもし作れても、リフレクション以外の方法でアクセスできないのであまり意味はないけどね。