# 技術|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]
```