---
# System prepended metadata

title: 'Day 20 : Golang Struct(1)'
tags: [About Go]

---

# Day 20 : Golang Struct(1)

## 簡介
在Golang裡面可以透過array、slice、map把資料放在一起，但以array和slice來說，只能存放單一型別的資料;一個map來說只能存在一種對應關係，例如一個使用者資料就只能是由使用者ID和名字組成，如果還需要其他資訊像是信箱、生日、身高等，就必須建立很多map來維護。這時比較好的做法則是把這些不同型別的資料放到一個單獨的資料結構，這個資料結構在Go裡面就是Struct。

定義
```
type identifier struct{
  field1 data_type
  field2 data_type
  field3 data_type
}
```


## 建立Struct

在建立和初始化Struct，先假設我們需要取的一個user的資訊，其中可能包含id、name、contactInfo，其中contactInfo又包含了email和zip code。故透過struct自行定義兩個新增的型別如下

```go=
type user struct {
    id  int
    lastname string
    contact   contactInfo  
    //這邊如果只寫contactInfo，代表欄位為contactInfo，資料型別為contactInfo
}

type contactInfo struct {
    email   string
    zipCode int
}
```

初始化的方式
* 第一種 : 沒有指定欄位名稱，不能隨意變換順序
* 第二種 : 有指定欄位名稱，可以變換順序
* 第三種 : 透過var，如果沒有賦值，會顯示zero value，此外也可以透過`.`賦值，

```go=

func main() {
    // 第一種 : 
    tony := user{1,"Chen",contactInfo{"tony@gmail.com",123}}
    fmt.Println(tony) // {1 Chen {tony@gmail.com 123}}
    //第二種 :
    dylan := user{
        lastname: "Huang",
        id:2,
        contact:contactInfo{
        email:   "dylan123@gmail.com",
        zipCode: 456,
    } }
    fmt.Printf("%+v", dylan) 
    // {id:2 lastname:Huang contact:{email:dylan123@gmail.com zipCode:456}}{id:0 lastname: contact:{email: zipCode:0}}
    // 第三種
    var vivi user 
    vivi.id=3
    vivi.contact.email="vivi@gmail.com"
    // {id:3 lastname: contact:{email:vivi@gmail.com zipCode:0}}
}

```

## Struct with Receiver function
建立一個Receiver function，可以讓任何user型別的變數使用，其變數名稱為p

```go=
func main() {
    dylan := user{lastname: "Huang", id: 2, contact: contactInfo{
        email:   "dylan123@gmail.com",
        zipCode: 456,
    }}
    dylan.print()
}

func (p user) print() {
    fmt.Printf("%+v", p)
}
```

## Struct & Value types
接下來建立一個修改名字的receiver function，但最後會發現，如果我將姓氏改為"Lin"，最後印出來的結果一樣姓氏一樣是"Huang"，參考下面第八行的結果。
```go=
func main() {
    dylan := user{lastname: "Huang", id: 2, contact: contactInfo{
        email:   "dylan123@gmail.com",
        zipCode: 456,
    }}

    dylan.updateLastName("Lin")
    dylan.print() // {id:2 lastname:Huang contact:{email:dylan123@gmail.com zipCode:456}}
}

func (p user) print() {
    fmt.Printf("%+v", p)
}

func (p user) updateLastName(newLastName string){
    p.lastname=newLastName
}
```

在Go裡面，int, float,string,bool,struct是以值傳遞，一開始會存在某個地方，之後如果要將某個值傳遞給函式的時候，Go會複製此結構體中所有資料，然後將值存在記憶體中新的位置，所以是在更新副本。

這時候就必須透過pointer來修改，程式碼更新如下
updateLastName()前面定義的`*user`，是一種type description，代表示要接受一個指向user的pointer
`*pointerToUser`則是將此記憶體位址做反解dereferencing，得到原本該位址的value，藉此修改原本變數dylan的值。
此概念可參考[Day 6 : Golang Pointer](https://hackmd.io/OtOdOT18SWOVxfD8BEu56Q)之說明


```go=
func main() {
    dylan := user{lastname: "Huang", id: 2, contact: contactInfo{
		email:   "dylan123@gmail.com",
		zipCode: 456,
	}}    
    (&dylan).updateLastName("Lin")  // &取得dylan這個變數的記憶體位址
    dylan.print()
}

func (p user) print() {
    fmt.Printf("%+v", p)
}

func (pointerToUser *user) updateLastName(newLastName string){
    (*pointerToUser).lastname=newLastName
}
```

簡化版可以將`(&dylan).updateLastName("Lin")`改為`dylan.updateLastName("Lin")`，此段程式碼一樣可以作用，因為此時receiver function的`*user`，如果是直接傳入一個user型別的變數，Go會自動地將dylan變數的地址傳遞給updateLastName方法，所以仍然可以更改原來的變數。


## References
1. [Go: The Complete Developer's Guide (Golang)](https://www.udemy.com/course/go-the-complete-developers-guide/)




###### tags: `About Go`