Go Struct Type Identity and Assignability
Tags: go
Published: 2020-07-09 00:40:11

Struct Type เป็น type ที่มีลักษณะ composite type โครงสร้างจริงๆว่า type เก็บอะไรได้บ้างเกิดจากเอา type ที่มีอยู่แล้วมาประกอบกันโดยเอามาสร้างเป็น field ของ struct

## Define new name type

ซึ่งเอาที่เราใช้งานกับ struct จริงๆแล้วเราจะเอามาประกาศ type ชื่อใหม่ก่อนเช่น

type Point struct { X, Y int }

เราก็จะได้ type ใหม่ชื่อ Point เอาไปใช้งานก็สร้างตัวแปรของ Point เพื่อเก็บข้อมูลแบบ struct ที่มี 2 fields คือ X เป็น int และ Y เป็น int เช่น

```Go
p1 := Point{X: 10, Y: 20}
fmt.Printf("%+v", p1) // {X:10, Y:20}
```

## Type Literal

แต่อย่างไรก็ตามการสร้างตัวแปรของ Composite Type อย่าง struct สามารถสร้างได้โดยไม่ต้องสร้าง type ชื่อใหม่แบบด้านบนก็ได้ (เราจะไม่ได้เห็นกันบ่อย แต่ก็รู้ไว้เพื่อเจอจะได้อ่านเข้าใจ)  เช่นเราอยากได้ตัวแปรแบบ p1 ที่มี 2 fields ชื่อ X เป็น int และ Y เป็น int เราประกาศตัวแปรได้แบบนี้

```Go
var p1 struct { X, Y int }
fmt.Printf("%+v", p1) // {X: 0, Y: 0}
```

ตรง struct { X, Y int } ว่ากันตาม spec ภาษาเขาใช้คำว่า type literal จริงเราก็ใช้ type literal กันอยู่แล้วกับ array (var arr int[5]), slice (var nums []), map (var store map[int]string) ซึ่งเราใช้ type literal ในการประกาศตัวแปรเลยโดยไม่สร้าง type ชื่อใหม่ แค่กับ struct มันไม่ค่อยสะดวกมันจะอ่านยาก เนื่องจากจำนวน field มันจะเยอะได้ และ เรามักตั้งชื่อเพราะ เราจำเป็นต้องสร้าง method ให้มันซึ่ง type literal สร้าง method ไม่ได้ต้องสร้าง type ชื่อใหม่อย่างเดียวเท่านั้น

## Type Identity

กลับมาที่เรื่อง 	type identity ตาม spec (https://golang.org/ref/spec#Type_identity) บอกว่า type ถ้าชื่อต่างกันจะถือว่าเป็นคนละ type เลย ทีนี่ก็จะมีประเด็นเรื่องพวก type literal ที่ไม่มีชื่อนี่แหละ จะมีกฏสำหรับบอกว่ามันเป็น type เดียวกันหรือต่างกันอยู่ สำหรับ struct ตาม spec บอกไว้ว่า

> Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.

คือต้องมี field เหมือนกันและลำดับของ field ตรงกันด้วย ชื่อเหมือนกันและ type ของ field เองก็ต้อง identical กันด้วย ถ้ามี tag ก็ต้องเหมือนกัน

อย่างกรณี struct { X, Y int } ก็มีแค่ struct {X, Y int} เหมือนกันนี่แหละที่ identical กัน

## Assignability

ตาม spec จะมีกฏเรื่อง Assignability (https://golang.org/ref/spec#Assignability) อยู่คือกฎ ว่าค่า type หนึ่ง จะกำหนดให้อีก type นึงได้ต้องมีเงื่อนไขอะไรบ้าง กฎแรกเลยก็คือ type ต้อง identical กัน สำหรับ struct แล้วก็ถ้าถ้าค่าเป็น type Point เหมือนกันก็ assign ให้กันได้ หรือถ้าเป็น type literal ก็ถ้าเป็นไปตามกฎเรื่อง identity ด้านบนก็ assign ให้กันได้

นอกจากนั้นยังมีกฎอีกข้อที่เอามาใช้กับ struct ได้คือ

>  x's type V and T have identical underlying types and at least one of V or T is not a defined type.

อ่านเข้าใจยาก แต่สรุปคือถ้าเรามี struct type ชื่อนึงเช่น Point ซึ่งกำหนดโครงสร้างแบบ struct { X, Y int } เราสามารถกำหนดค่าของที่เป็น type literal struct {X, Y int } ให้ตัวแปรของ Point ได้เลย เช่น

```Go
var p struct { X, Y int } = struct {X, Y int} {X: 10, Y: 20}
var p1 Point = p

// หรือ

var p1 Point = struct {X, Y int} {X: 10, Y: 20}
```

ในทางตรงกันข้ามถ้าเรามีตัวแปรที่มี type เป็น literal struct โครงสร้างเหมือนกันกับ Point ก็กำหนดค่าของ Point ให้กับตัวแปรนั้นได้เช่นกัน เช่น

```Go
var p Point = Point{X: 10, Y: 20}
var p1 struct {X, Y int} = p

// หรือ

var p struct {X, Y int} = Point{X: 10, Y: 20}
```