---
# System prepended metadata

title: 技術｜Golang 筆記
tags: [技術]

---

# 技術｜Golang 筆記
## (1) go routine
- 輕量級的線程
```go=
package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go printNumbers()  // 開啟新的 Goroutine 執行 printNumbers 函數
    fmt.Println("Goroutine started")
    time.Sleep(1 * time.Second)  // 等待 Goroutine 完成
}
```
## (2) channel
- `channel` 在 goroutine 中實現同步和數據的傳遞
```go=
package main

import "fmt"

func sum(a []int, c chan int) {
    total := 0
    for _, v := range a {
        total += v
    }
    c <- total  // 將結果發送到 channel
}

func main() {
    a := []int{1, 2, 3, 4, 5}
    c := make(chan int)
    go sum(a, c)
    result := <-c  // 從 channel 接收結果
    fmt.Println("Sum:", result)
}
```
## (3) context
- `context` 用來在 Goroutine 之間傳遞數據、信號和取消指令
```go=
package main

import (
    "context"
    "fmt"
    "time"
)

func task(ctx context.Context) {
    select {
    case <-time.After(2 * time.Second):
        fmt.Println("Task completed")
    case <-ctx.Done():  // 任務取消或超時
        fmt.Println("Task cancelled:", ctx.Err())
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()  // 確保取消函數在 main 結束時被調用

    go task(ctx)
    time.Sleep(3 * time.Second)
}
```
## (4) defer
- `defer` 用於確保資源釋放，延遲執行某個語句，按 LIFO (Last In First Out) 順序執行
```go=
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer file.Close()  // 確保在函數結束時關閉文件

    fmt.Println("File opened successfully")
}
```

## (5) switch
- 接口 interface{} 型別判斷 variable.(type)，重點是會`依照 case 順序依序執行`
```go=
func convert(i interface{}) {
    switch t := i.(type) {
    case int:
        println("i is interger", t)
    case string:
        println("i is string", t)
    case float64:
        println("i is float64", t)
    default:
        println("type not found")
    }
}
```

## (6) select
- 只能接 channel 否則會出錯，同一個 channel 在 select 會隨機選取，default 會直接執行
```go=
func main() {
    ch := make(chan int, 1)

    ch <- 1
    select {
    case <-ch:
        fmt.Println("random 01")
    case <-ch:
        fmt.Println("random 02")
    default:
        fmt.Println("exit")
    }
}
```

## Golang 概念
- 函數外的每個語句都必須以關鍵字開始
- make 和 new 差異 [Ref](https://stackoverflow.com/questions/25358130/what-is-the-difference-between-new-and-make)
    - make 用在 slices, maps, 和 channels，回傳物件本身
    - new 對任何 type 都適用，回傳指標
- [並發](https://peterhpchen.github.io/2020/03/08/goroutine-and-channel.html)同步機制: (1) Waitgroup (2) channel (3) mutex

## Methods 方法/ Interface 接口
- (1) 為某資料型態 (預設的或是自訂義struct) 建立 Method
    - receiver 可設定成值或是指標，要用正常的 pointer/reference 去操作
        ```go=
        type Vertex struct {
            X, Y float64
        }
        func (v *Vertex) Scale(f float64) {
            v.X = v.X * f
            v.Y = v.Y * f
        }
        func (v Vertex) Abs() float64 {
            return math.Sqrt(v.X*v.X + v.Y*v.Y)
        }
        func main() {
            v := Vertex{3, 4}
            p := &Vertex{4, 3}
            (&v).Scale(2)
            p.Scale(2)
            fmt.Println(v, p)                // {6 8} &{8 6}
            fmt.Println(v.Abs(), (*p).Abs()) // 10 10
        }
        ```
    - `以下是 GO 當中的例外，個人認為這個特性很糟`
        ```go=
        func main() {
            v := Vertex{3, 4}
            p := &Vertex{4, 3}
            
            v.Scale(2)            // 會被自動解讀為 (&v).Scale(2)
            fmt.Println(v)        
            
            fmt.Println(p.Abs())  // 會被自動解讀為 (*p).Abs()
        }
        ```
- (2) Interface 是藉由定義一系列方法，可以變身成各種型態
    ```go=
    type face interface {
        Scale(f float64)
    }
    type Vertex struct {
        X, Y float64
    }
    // 標準寫法 (1)
    func (v *Vertex) Scale(f float64) 
    var i face = &Vertex{3, 4}
    
    // 標準寫法 (2)
    func (v Vertex) Scale(f float64)
    var i face = Vertex{3, 4}
    
    // 底下寫法編譯會過，但看別人教學沒人這樣用
    func (v Vertex) Scale(f float64)
    var j face = &Vertex{8, 6}
    
    // 不正確且編譯不過
    func (v *Vertex) Scale(f float64)
    var i face = Vertex{3, 4}
    ```
## 陣列/Slice
- `陣列`
    ```go=
    arr := [4]int{1, 2, 3, 4}
    
	fmt.Println(arr)
    for _, v := range arr {
        fmt.Printf("%d ", v)
    }
    ```
- `Slice 切片`
    - 切片是一個有長度和容量，類似陣列的結構
    - 動態宣告、初始化
    ```go=
    a := make([]int, 5)
    a := []int{1, 2, 3, 4, 5}
        
    func printSlice(s []int) {
        fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
    }
	```
    - (1) 使用 `[a:b]` 只是一個參照
    ```go=
    a := []int{1, 2, 3, 4, 5}
    b := a[2:4]
	a[2] = 100
	b[1] = 99
    printSlice(a)
    printSlice(b)
    //----------------------------
    len=9 cap=9 [1 2 100 99 5 6 7 8 9]
    len=2 cap=7 [100 99]
    ```
    - (2) 使用 append 要注意參照的問題
    ```go=
    arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 8, 7}
	arr2 := arr[2:4]
	printSlice(arr)
	printSlice(arr2)
	arr2 = append(arr2, -1)
	arr = append(arr, 6)
	printSlice(arr)
	printSlice(arr2)
    //-----------------------------
    len=10 cap=10 [1 2 3 4 5 6 7 8 8 7]
    len= 2 cap=8  [3 4]
    len=11 cap=20 [1 2 3 4 -1 6 7 8 8 7 6]
    len= 3 cap=8  [3 4 -1]
    ```
    - (3) 使用 copy，只會改到
    ```go=
    a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	b := a[2:4]
	c := []int{0, 0, 0, 0, 0, 0}
	printSlice(a)
	printSlice(b)
	copy(b, c)
	printSlice(a)
	printSlice(b)
    //----------------------------
    len=9 cap=9 [1 2 3 4 5 6 7 8 9]
    len=2 cap=7 [3 4]
    len=9 cap=9 [1 2 0 0 5 6 7 8 9]
    len=2 cap=7 [0 0]
    ```


