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

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

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

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

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

    在包内部,特别是在非导出函数中有很深层次的嵌套调用时,对主调函数来说用 panic 来表示应该被翻译成错误的错误场景是很有用的(并且提高了代码可读性)。

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

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

    示例 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 parse: error parsing "12.5" as int
    5. Parsing "2 + 2 = 4":
    6. pkg parse: error parsing "+" as int
    7. Parsing "1st class":
    8. pkg parse: error parsing "1st" as int
    9. Parsing "":
    10. pkg: no words to parse