0%

[Go] log.Fatal v.s. panic

這陣子剛開始學習 Golang, 看了一些 tutorial, 發現有些範例會使用 log.Fatal() 輸出錯誤訊息,而有些會使用 panic(), 這篇文章先記錄一下兩者的差別,後續的 Golang 筆記再慢慢補~

os.Exit()

在比較兩者差別之前,需要先了解 os.Exit() 的定義:

1
2
3
4
5
func Exit(code int)

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// The program terminates immediately; deferred functions are not run.
  • code == 0: success
  • code != 0: error, program 會馬上終止,defer function 不會執行

log.Fatal()

Reference: Logger.Fatal

1
2
3
func (l *Logger) Fatal(v ...any)

// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).

Source code:

1
2
3
4
func (l *Logger) Fatal(v ...any) {
l.Output(2, fmt.Sprint(v...))
os.Exit(1)
}

在 source code 中可以看到 log.Fatal() 會先輸出錯誤訊息,接著 call os.Exit(1), 也就是會馬上終止 program, 並且不會執行 defer function.

Panic

Reference: buildin.go

1
2
3
4
5
6
7
8
9
10
11
// The panic built-in function stops normal execution of the current
// goroutine. When a function F calls panic, normal execution of F stops
// immediately. Any functions whose execution was deferred by F are run in
// the usual way, and then F returns to its caller. To the caller G, the
// invocation of F then behaves like a call to panic, terminating G's
// execution and running any deferred functions. This continues until all
// functions in the executing goroutine have stopped, in reverse order. At
// that point, the program is terminated with a non-zero exit code. This
// termination sequence is called panicking and can be controlled by the
// built-in function recover.
func panic(v any)
  • function F 會馬上終止
  • defer function 被執行
  • Return to caller G
  • To the caller G, call F() 就像 call panic(), 因此進行:
    • 終止 G 的執行
    • 執行 defer functions
    • Return to G’s caller
  • 持續進行上個步驟,直到最上層的 function, 此時 program 會終止

以上整個流程叫做 panicking, 而可以透過 recover 來調控 panicking 的流程。

Recover

panic and recover 類似 C++/Java/Python 中的 try catch. recover 就是用來調控 panicking 流程,如果發生了 panic, 而我們需要做一些處理,就可以使用 recover(), recover 必須在被 defer 的 function 中執行才有效果,如果在被 defer 的 function 外執行, recover 一定是回傳 nil.

用法如下:

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

import "fmt"

func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()

// panic blocks
}

Defer

上面提到的 defer function 是指可以使用 defer 指定某個 function 延遲執行,會延遲到 main function return 之前再執行,for example:

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

import "fmt"

func deferredFunc() {
fmt.Println("deferredFunc")
}

func main() {
defer deferredFunc()
fmt.Println("Hello, world!")
}

我們在 deferredFunc() 前加上 defer,因此它會在 main() return 前執行,所以執行結果會是先顯示 “Hello, world!”,才顯示 “deferredFunc”。

如果有多個 defer functions, 在 main function return 前,會依 defer 的相反順序執行,也就是 LIFO.

References