golang type conversion not working as (I) expected

19,493

An error is an interface with a single method Error() string. See http://golang.org/pkg/builtin/#error

A flags.Error has such a method, so it can be used as an error.

Conversely, however, a flags.Error is a struct, and there's no way to convert an arbitrary value to a struct.

What you can do is, and I think this is the answer to your question, is that if you've got a flags.Value inside an error, then you can cast the error back to the underlying type. The syntax for that is e := err.(*flags.Error). This'll give you a value of type *flags.Error (or panic, if the underlying type isn't *flags.Error). You can avoid the panic in this case by using the comma-ok form, which is e, ok := err.(*flags.Error).

Concretely, you would write:

  args, err := flags.Parse()
  if err != nil {
     if ferr, ok := err.(*flags.Error); ok {
       // ... something using ferr
     } else {
       // ... deal with non-flags.Error case, if that's possible.
     }
  }
Share:
19,493
Brad Peabody
Author by

Brad Peabody

Me? I code stuff.

Updated on June 30, 2022

Comments

  • Brad Peabody
    Brad Peabody almost 2 years

    I'm using go-flags to parse command line options.

    Per the go-flags docs:

    ... [if] either -h or --help was specified in the command line arguments, a help message will be automatically printed. Furthermore, the special error type ErrHelp is returned.

    The method I'm calling is:

    func (p *Parser) Parse() ([]string, error) {
    

    I'm calling it with:

    var opts struct {
        // ...
    }
    
    func main() {
    
        parser := flags.NewParser(&opts, flags.Default)
    
        args, err := parser.Parse()
    

    A snippet from the file that defines ErrHelp looks like this:

    type ErrorType uint
    
    const (
        // Unknown or generic error
        ErrUnknown ErrorType = iota
    
        // Expected an argument but got none
        ErrExpectedArgument
    
        // ...
    
        // The error contains the builtin help message
        ErrHelp
    
        // ...
    )
    
    // Error represents a parser error. The error returned from Parse is of this
    // type. The error contains both a Type and Message.
    type Error struct {
        // The type of error
        Type ErrorType
    
        // The error message
        Message string
    }
    
    // Get the errors error message.
    func (e *Error) Error() string {
        return e.Message
    }
    
    func newError(tp ErrorType, message string) *Error {
        return &Error{
            Type:    tp,
            Message: message,
        }
    }
    

    So they have this custom "Error" type. And in the Parse() method above, internally, the error is getting created with a block of code like this:

        help.ShowHelp = func() error {
            var b bytes.Buffer
            p.WriteHelp(&b)
            return newError(ErrHelp, b.String())
        }
    

    As you can see newError() returns "*Error" as it's type. But the anonymous function just above returns type "error" - so those types must be compatible.(?)

    But now back to the original problem - I'm just trying to see if my "err" is an "Error" and has member "Type" equal to ErrHelp. So I try this:

    if err != nil && flags.Error(err).Type == flags.ErrHelp {
    

    Or even just this:

    fmt.Printf("test:", flags.Error(err))
    

    And either way the compiler gives me:

    main.go:37: cannot convert err (type error) to type flags.Error

    But does not state why that conversion can't be done. Any ideas?

    (I don't get how "*Error" successfully converts to "error" in the anonymous function above, and I even more don't get why if that works then I can't convert it back the other way... I must be missing something really dumb here, but I'm not seeing what it is.)

  • Brad Peabody
    Brad Peabody almost 11 years
    Ok gotcha - thanks and that does work! I can't find in the docs what that syntax means... "err.(*flags.Error)" means "give me a pointer to err but with type flags.Error"? (I'm assuming like you said I can't convert directly to Error, but I can convert to an Error pointer - that would make sense.)