13.4 自定义包中的错误处理和 panicking

这是所有自定义包实现者应该遵守的最佳实践:

1)在包内部,总是应该从 panic 中 recover:不允许显式的超出包范围的 panic()

2)向包的调用者返回错误值(而不是 panic)。

在包内部,特别是在非导出函数中有很深层次的嵌套调用时,将 panic 转换成 error 来告诉调用方为何出错,是很实用的(且提高了代码可读性)。

下面的代码则很好地阐述了这一点。我们有一个简单的 parse 包(示例 13.4)用来把输入的字符串解析为整数切片;这个包有自己特殊的 ParseError

当没有东西需要转换或者转换成整数失败时,这个包会 panic()(在函数 fields2numbers() 中)。但是可导出的 Parse() 函数会从 panic()recover() 并用所有这些信息返回一个错误给调用者。为了演示这个过程,在 panic_recover.go 中 调用了 parse 包(示例 13.5);不可解析的字符串会导致错误并被打印出来。

示例 13.4 parse.go

  1. // parse.go
  2. package parse
  3. import (
  4. "fmt"
  5. "strings"
  6. "strconv"
  7. )
  8. // A ParseError indicates an error in converting a word into an integer.
  9. type ParseError struct {
  10. Index int // The index into the space-separated list of words.
  11. Word string // The word that generated the parse error.
  12. Err error // The raw error that precipitated this error, if any.
  13. }
  14. // String returns a human-readable error message.
  15. func (e *ParseError) String() string {
  16. return fmt.Sprintf("pkg parse: error parsing %q as int", e.Word)
  17. }
  18. // Parse parses the space-separated words in in put as integers.
  19. func Parse(input string) (numbers []int, err error) {
  20. defer func() {
  21. if r := recover(); r != nil {
  22. var ok bool
  23. err, ok = r.(error)
  24. if !ok {
  25. err = fmt.Errorf("pkg: %v", r)
  26. }
  27. }
  28. }()
  29. fields := strings.Fields(input)
  30. numbers = fields2numbers(fields)
  31. return
  32. }
  33. func fields2numbers(fields []string) (numbers []int) {
  34. if len(fields) == 0 {
  35. panic("no words to parse")
  36. }
  37. for idx, field := range fields {
  38. num, err := strconv.Atoi(field)
  39. if err != nil {
  40. panic(&ParseError{idx, field, err})
  41. }
  42. numbers = append(numbers, num)
  43. }
  44. return
  45. }

示例 13.5 panic_package.go

  1. // panic_package.go
  2. package main
  3. import (
  4. "fmt"
  5. "./parse/parse"
  6. )
  7. func main() {
  8. var examples = []string{
  9. "1 2 3 4 5",
  10. "100 50 25 12.5 6.25",
  11. "2 + 2 = 4",
  12. "1st class",
  13. "",
  14. }
  15. for _, ex := range examples {
  16. fmt.Printf("Parsing %q:\n ", ex)
  17. nums, err := parse.Parse(ex)
  18. if err != nil {
  19. fmt.Println(err) // here String() method from ParseError is used
  20. continue
  21. }
  22. fmt.Println(nums)
  23. }
  24. }

输出:

  1. Parsing "1 2 3 4 5":
  2. [1 2 3 4 5]
  3. Parsing "100 50 25 12.5 6.25":
  4. pkg: pkg parse: error parsing "12.5" as int
  5. Parsing "2 + 2 = 4":
  6. pkg: pkg parse: error parsing "+" as int
  7. Parsing "1st class":
  8. pkg: pkg parse: error parsing "1st" as int
  9. Parsing "":
  10. pkg: no words to parse

链接