使用Golang中的类型断言检测值超出范围的错误

问题描述:

Given the following code:

iv, err := strconv.ParseInt("18446744073709551448", 10, 64)
fmt.Println(iv)
fmt.Printf("%#v
", err)
fmt.Printf("%v
", err)

//Output:
9223372036854775807
&strconv.NumError{Func:"ParseInt", Num:"18446744073709551448", Err:(*errors.errorString)(0x1040a040)}
strconv.ParseInt: parsing "18446744073709551448": value out of range

How can I detect that the function failed due to being out of range of an int64? The strconv.ParseInt function returns an error type, but in this case it is actually a strconv.NumError type as indicated by %#v. The Error Handling and Go article mentions you can use type assertion to check for specific types of errors, but it doesn't give any examples. What expression should I use to complete this code:

if expression {
    uv, err := strconv.ParseUint("18446744073709551448", 10, 64)
}

We have,

Package strconv

var ErrRange = errors.New("value out of range")

ErrRange indicates that a value is out of range for the target type.

type NumError struct {
        Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
        Num  string // the input
        Err  error  // the reason the conversion failed (ErrRange, ErrSyntax)
}

A NumError records a failed conversion.

func (e *NumError) Error() string

For example,

package main

import (
    "fmt"
    "strconv"
)

func main() {
    iv, err := strconv.ParseInt("18446744073709551448", 10, 64)
    if err != nil {
        if numError, ok := err.(*strconv.NumError); ok {
            if numError.Err == strconv.ErrRange {
                fmt.Println("Detected", numError.Num, "as a", strconv.ErrRange)
                return
            }
        }
        fmt.Println(err)
        return
    }
    fmt.Println(iv)
}

Output:

Detected 18446744073709551448 as a value out of range

The error that's returned from strconv.ParseInt is only known at compile time to be some type that implements the Error interface. Type assertion allows you to insist that it's a strconv.NumError and inspect its fields directly, but at the risk of throwing a runtime panic if you turn out to be wrong:

if err.(*strconv.NumError).Err.Error() == "value out of range" {
    uv, err := strconv.ParseUint("18446744073709551448", 10, 64)
}

A more flexible solution (but maybe too loosey-goosey for your purposes) would be to perform a substring match on the err.Error() method:

if strings.Contains(err.Error(), "value out of range") {
    uv, err := strconv.ParseUint("18446744073709551448", 10, 64)
}