Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

golang type conversion not working as (I) expected

Tags:

types

flags

go

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.)

like image 704
Brad Peabody Avatar asked Aug 24 '13 07:08

Brad Peabody


People also ask

How do I convert type in Golang?

Type conversion happens when we assign the value of one data type to another. Statically typed languages like C/C++, Java, provide the support for Implicit Type Conversion but Golang is different, as it doesn't support the Automatic Type Conversion or Implicit Type Conversion even if the data types are compatible.

How to convert string* into string in Go?

You can convert numbers to strings by using the strconv. Itoa method from the strconv package in the Go standard libary. If you pass either a number or a variable into the parentheses of the method, that numeric value will be converted into a string value.

How to convert an int into a string Golang?

In order to convert an integer value to string in Golang we can use the FormatInt function from the strconv package. FormatInt returns the string representation of i in the given base, for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' for digit values >= 10.

How to convert variable to string in Golang?

An integer can be converted to a string in Golang using the Itoa function within the strconv library.


1 Answers

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.
     }
  }
like image 174
Paul Hankin Avatar answered Nov 12 '22 14:11

Paul Hankin