iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 29
0
Software Development

學習Go系列 第 29

Go day 29 (error handling)

error handling

go 的錯誤處理大致上分成兩塊 Error 和 Panic.Error 不會中斷程式但 Panic 會.
下面範例出現錯誤訊息程式並不會中斷還會繼續執行

package main

import (
	"encoding/csv"
	"fmt"
	"os"
)

func main() {
	csvFile, error := os.Create("./null/newFile.csv")
	if error != nil {
		fmt.Println(error)
	}
	fmt.Println(csvFile) // <nil>
	w := csv.NewWriter(csvFile)
	fmt.Println(w) // &{44 false 0xc00001e100}
	w.Write([]string{"H", "e", "l", "l", "o"})
	w.Flush()
	fmt.Println("Done")
}

執行結果

> go run example1.go
open ./null/newFile.csv: no such file or directory
<nil>
&{44 false 0xc00001e100}
Done

但加上 panic 後程式就會中斷了,不會繼續往下執行

package main

import (
	"encoding/csv"
	"fmt"
	"os"
)

func main() {
	csvFile, error := os.Create("./null/newFile.csv")
	if error != nil {
		fmt.Println(error)
		panic(error)
	}
	fmt.Println(csvFile) // <nil>
	w := csv.NewWriter(csvFile)
	fmt.Println(w) // &{44 false 0xc00001e100}
	w.Write([]string{"H", "e", "l", "l", "o"})
	w.Flush()
	fmt.Println("Done")
}

執行結果

> go run example1.go
open ./null/newFile.csv: no such file or directory
panic: open ./null/newFile.csv: no such file or directory

goroutine 1 [running]:
main.main()
 /Volumes/Transcend/golang/goHello/src/practice/example1.go:13 +0x333
exit status 2

recover

如果程式發生 panic 後,recover 機制會停止 panic 並回傳 panic 的值.
使用了 recover 程式雖然不會從中斷處繼續執行但可以有機會做一些當發生 panic 時的處理.
還可以使用 runtime.Stack 可以找出發生問題的 stack trace.

package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"runtime"
)

func main() {
	defer func() {
		switch err := recover(); err {
		case nil:
			fmt.Println("no Panic...")
		default:
			fmt.Println(err) // panic 的值
			fmt.Println("------- stack trace -------")
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			fmt.Println(string(buf[:n]))
			fmt.Println("recover and stop...")
		}
	}()

	csvFile, error := os.Create("./null/newFile.csv")
	if error != nil {
		panic(error)
	}
	fmt.Println(csvFile) // <nil>
	w := csv.NewWriter(csvFile)
	fmt.Println(w) // &{44 false 0xc00001e100}
	w.Write([]string{"H", "e", "l", "l", "o"})
	w.Flush()
	fmt.Println("Done")
}


執行結果

go run example1.go
open ./null/newFile.csv: no such file or directory
------- stack trace -------
goroutine 1 [running]:
main.main.func1()
 /Volumes/Transcend/golang/goHello/src/practice/example1.go:19 +0xff
panic(0x10ac960, 0xc0000701b0)
 /usr/local/go/src/runtime/panic.go:513 +0x1b9
main.main()
 /Volumes/Transcend/golang/goHello/src/practice/example1.go:27 +0x303

recover and stop...

透過不同的 type 來決定要做怎樣的 recover

package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"runtime"
)

type CustomError struct {
	msg string
}

func main() {
	defer func() {
		defer fmt.Println("recover and stop...")
		switch err := recover(); err {
		case nil:
			fmt.Println("no Panic...")
		case CustomError{}:
			fmt.Println("CustomError...")
		default:
			fmt.Println(err) // panic 的值
			fmt.Println("------- stack trace -------")
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			fmt.Println(string(buf[:n]))
			fmt.Println("Panic ...")
		}
	}()

	csvFile, error := os.Create("./null/newFile.csv")
	if error != nil {
		panic(CustomError{})
	}
	fmt.Println(csvFile) // <nil>
	w := csv.NewWriter(csvFile)
	fmt.Println(w) // &{44 false 0xc00001e100}
	w.Write([]string{"H", "e", "l", "l", "o"})
	w.Flush()
	fmt.Println("Done")
}

執行結果

> go run example1.go
CustomError...
recover and stop...

custom Error

  1. 定義一個 Error 的 struct(CustomError)
  2. 替 struct(CustomError) 加上一個 Error() function
  3. 定義錯誤的 function 回傳定義好的 Error (CustomError)
package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"runtime"
)

type CustomError struct {
	msg string
}

func (error *CustomError) Error() string {
	return error.msg
}

func FileNotExistError() error {
	return &CustomError{"FileNotExistError"}
}

func main() {
	defer func() {
		defer fmt.Println("recover and stop...")
		switch err := recover(); err {
		case nil:
			fmt.Println("no Panic...")
		case CustomError{}:
			fmt.Println("CustomError...")
		default:
			fmt.Println(err) // panic 的值
			fmt.Println("------- stack trace -------")
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			fmt.Println(string(buf[:n]))
			fmt.Println("Panic ...")
		}
	}()

	csvFile, error := createFile("./null/newFile.csv")
	if error != nil {
		fmt.Println(error)
	}
	fmt.Println(csvFile) // <nil>
	w := csv.NewWriter(csvFile)
	fmt.Println(w) // &{44 false 0xc00001e100}
	w.Write([]string{"H", "e", "l", "l", "o"})
	w.Flush()
	fmt.Println("Done")
}

func createFile(filePath string) (*os.File, error) {
	csvFile, error := os.Create(filePath)
	if error != nil {
		return nil, FileNotExistError()
	}
	return csvFile, error
}

執行結果

> go run example1.go
FileNotExistError
<nil>
&{44 false 0xc00001e100}
Done
no Panic...
recover and stop...

用上面的例子複習一下建立及初始化一個實體的 type.

func FileNotExistError() error {
	return &CustomError{"FileNotExistError"}
}

上面的寫法相當於 new(T)

func FileNotExistError() error {
	custError := new(CustomError)
	custError.msg = "FileNotExistError"
	return custError
}

上一篇
Go day 28 (benchmark)
下一篇
Go day 30 (HDFS to Redis & keep going)
系列文
學習Go30

尚未有邦友留言

立即登入留言