Hike News
Hike News

Go初識-day9-內置函數

內置函數

內置函數可以直接使用,且不需要導入特定的package

  • close: 主要用來關閉channel
  • len: 用來求長度,比如sting,array,slice,map,channel
  • new: 用來分配內存,主要用來分配值類型,比如int,struct等基本類型,返回的是指針(注意!!)
  • make: 用來分配內存,主要用來分配引用類型,比如channel,map,slice
  • append:用來追加元素到slice
  • panicrecover: 用來作錯誤處理

new

通常用來分配值類型的數據
返回的是指針

聲明

1
變量 := new(類型)

比較

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main(){
var i int //[1]
fmt.Println("i =",i)

j := new(int) //[2]
fmt.Println("j =",j)
*j = 100 //[3]
fmt.Println("*j =",*j)
}

[1] 一般聲明值類型的方式,返回的是值
[2] 使用new來定義一個int類型的指針
[3] 使用*對指向地址裡儲存的值賦值

result

1
2
3
i = 0
j = 0xc042060088 //[1]
*j = 100

[1] j變量為一指針類型


make

通常用來分配引用類型
比如channel,map,slice


new 與 make的區別

new different from make

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main(){
a := new([]int)
b := make([]int,0)

fmt.Println(a)
fmt.Println(b)
}

result

1
2
&[]
[]

可以看到new返回的是指針類型,指向一個空切片的地址
make直接返回的就是切片,而不是指針


append

用來追加元素到slice
append函數的第二參數為可變參數,可追加一個值或多個值


追加一個值

1
2
var a []int
a = append(a,1)

result

1
a = [1]


追加多個值

1
2
var a []int
a = append(a,1,3,5,7,9)

result

1
a = [1 3 5 7 9]


追加一個slice中的元素

1
2
3
var a []int = []int{1,2,3,}
var b []int = []int{11,22,33,}
a = append(a,b...) //[1]

[1] 使用...b切片展開追加到a切片中

result

1
a = [1 2 3 11 22 33]


panic & recover

panic 錯誤處裡

程序執行時遇到某些極端情況,無法繼續執行,可拋出panic異常
且Go本身就有自帶一些已知panic異常,可快速發現問題

  • 且通常拋出panic且未捕獲的狀態下,程序會停止運行

系統自拋 panic

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main(){
a := 0
b := 100/a //[1]
fmt.Println(b)
}

[1] b變量應是無解值

result

1
2
3
4
5
panic: runtime error: integer divide by zero  //[1]

goroutine 1 [running]:
main.main()
D:/GoWork/test.go:7 +0x11 //[2]

[1] 拋出panic異常並跟隨原因
[2] 並顯示於第7行語句發生異常


主動拋出 panic

遇到一些代碼可正常執行,但邏輯錯誤,可自己主動拋出異常

模擬配置文件載入出錯

模擬配置文件讀取失敗,但是程序仍能正常運行,屬邏輯錯誤
設計者應主動拋出panic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"errors"
)

//定義一個函數 模擬初始化配置文件出錯
func initConfig_Failed()(err error){ //[1]
return errors.New("initConfig Failed!!") //[2]
}

func panic_test(){
err := initConfig_Failed()
if err != nil {
panic(err) //[3]
}
}

func main(){
panic_test()
}

[1] 返回err變量,其為error類型(保存錯誤訊息)
[2] errors.New初始化一個error類型的自訂義錯誤訊息實例
[3] 主動調用panic函數,參數為錯誤信息

result

1
2
3
4
5
6
7
panic: initConfig Failed!!

goroutine 1 [running]:
main.panic_test()
D:/GoWork/test.go:15 +0x9f
main.main()
D:/GoWork/test.go:20 +0x27

  • panic錯誤訊息為自訂義之訊息,並明指了發生錯誤的行數

recover 捕獲異常

  • 有時程序上線時,即使發生異常,也不希望程序退出
  • 發生的錯誤並不是致命性的
  • 發生錯誤並不影響程序繼續運行,或是不影響下一次請求

基於以上原因,希望能藉由捕獲異常(記錄日誌),以防止程序崩潰或停擺

搭配 defer 及 匿名函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"fmt"
"time"
)

func test_recover(){
defer func () { //[1]
err := recover() //[2]
if err != nil {
fmt.Println("Error:",err)

//捕獲異常後,欲執行的事項,如紀錄日誌,除錯處理...等
}
}() //[3]


//以下為發生異常代碼
a := 0
b := 100/a
fmt.Println(b)
return
}

func main(){
for { //模擬不斷的請求處裡
test_recover()
time.Sleep(time.Second) //延遲一秒
}
}

[1] 使用defer於函數發生panic要退出函數時,搭配匿名函數捕獲異常
[2] 如發生異常,調用recover函數捕獲,並將獲取的panic信息賦值給err變量
[3] 匿名函數聲明時即調用需再聲明完畢}左邊加上()調用並填入參數

result

1
2
3
4
5
6
7
Error: runtime error: integer divide by zero
Error: runtime error: integer divide by zero
Error: runtime error: integer divide by zero
Error: runtime error: integer divide by zero
Error: runtime error: integer divide by zero
...
...

從結果可發現程序不斷地在發出panic,但是沒有退出程序