Does go have standard Err variables?

2019-01-06 23:25发布

Just started using Golang. I think that it is idiomatic to declare an error variable and use it in your error structure to determine what went wrong, as is done in strconv.go. There, ErrRange and ErrSyntax is declared, and when appropriate, references to those are stored in NumError structs when they return. I think that the reason is because then the address of the reference to the error stored in NumError can be compared with the ErrRange and ErrSyntax variables to determine which type of error was returned.

Are there "standard" such declared error types? In Java, for example, you have things like java.lang.IllegalArgumentException. Is there, for instance, ErrArgument or ErrUnsupportedOperation that I can use in my own code instead of creating new error variables that mean the same thing every time?

标签: go
3条回答
兄弟一词,经得起流年.
2楼-- · 2019-01-06 23:53

There are a few common idiomatic ways for a package author to make error returns.

  1. Fixed error variables, usually named Err…

    var (
            ErrSomethingBad = errors.New("some string")
            ErrKindFoo      = errors.New("foo happened")
    )
    
  2. Error types, usually named …Error

    type SomeError struct {
         // extra information, whatever might be useful to callers
         // (or for making a nice message in `Error()`)
         ExtraInfo int
    }
    type OtherError string
    
    func (e SomeError) Error() string { /* … */ }
    func (e OtherError) Error() string {
            return fmt.Sprintf("failure doing something with %q", string(e))
    }
    
  3. Ad hoc errors.New values as needed.

    func SomepackageFunction() error {
            return errors.New("not implemented")
    }
    
  4. Using errors defined in the standard packages. Usually limited to a small set such as io.EOF; in most cases it's better to create your own via method 1 above.

    func SomeFunc() error {
            return io.EOF
    }
    

    Note that sometimes when implementing an interface (such as a Read method to become an io.Reader) it is best to use matching errors (or "required" by the specification of the interface).

  5. Making an interface such as net.Error:

    type Error interface {
        error
        Timeout() bool   // Is the error a timeout?
        Temporary() bool // Is the error temporary?
    }
    

Often you'll use a mix of all these ways.

The first, second, and fifth are preferred if you think any user of your package will ever want to test for specific errors. They allow things like:

err := somepkg.Function()
if err == somepkg.ErrSomethingBad {
        // …
}
// or for an error type, something like:
if e, ok := err.(somepkg.SomeError); ok && e.ExtraInfo > 42 {
        // use the fields/methods of `e` if needed
}

The fifth way (which is just an extension of the second) allows checking the error for behaviour/type like so:

if e, ok := err.(net.Error); ok && e.Timeout() {
        // it's a timeout, sleep and retry
}

The problem with the third way is it leaves no sane way for a user of the package to test for it. (Testing the contents of the string returned by err.Error() isn't a great idea). However, it's fine for the errors that you don't ever expect anyone to want to test for.

Further reading:

查看更多
不美不萌又怎样
3楼-- · 2019-01-07 00:05

As you have seen, there are specific errors that specific packages use. For example, in the database/sql package, they define:

var ErrNoRows = errors.New("sql: no rows in result set")

So if you do QueryRow (which defers the error until Scan), and then Scan, you can do

if  err := row.Scan(&data); err != nil && err != sql.ErrNoRows {
    //something actually went wrong
} else if err != nil {
    //no results
} else {
    //we found it
}

os/exec has var ErrNotFound = errors.New("executable file not found in $PATH")

encoding/json has a type UnmarshalTypeError which is just a type that implements the error interface.

So no, while there is no "set of standard errors", you can (and most likely should) have specific error variables that you reuse.

You could have your own errorMsgs package that you use, where you can reuse common errors:

err := doSomething(); if err != nil {
    switch err {
        case errorMsgs.IllegalArgument:
           //do something
        case errorMsgs.CouldNotConnect:
           //do something else
     }
}
查看更多
看我几分像从前
4楼-- · 2019-01-07 00:15

No, there aren't. Just provide intelligible errors instead of generic ones. What information does a IllegalArgument transport? Not much, not enough.

查看更多
登录 后发表回答