Golang类型转换不按照我期望的方式工作。

huangapple go评论109阅读模式
英文:

golang type conversion not working as (I) expected

问题

我正在使用go-flags来解析命令行选项。

根据go-flags文档:

...如果命令行参数中指定了-h或--help,将自动打印帮助信息。此外,还会返回特殊的错误类型ErrHelp。

我调用的方法是:

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

我这样调用它:

var opts struct {
    // ...
}

func main() {

    parser := flags.NewParser(&opts, flags.Default)

    args, err := parser.Parse()

定义ErrHelp的文件片段如下:

type ErrorType uint

const (
	// 未知或通用错误
	ErrUnknown ErrorType = iota

	// 预期需要参数但未提供
	ErrExpectedArgument

	// ...

	// 错误包含内置的帮助信息
	ErrHelp

	// ...
)

// Error表示解析器的错误。Parse返回的错误属于此类型。错误包含类型和消息。
type Error struct {
	// 错误类型
	Type ErrorType

	// 错误消息
	Message string
}

// 获取错误消息。
func (e *Error) Error() string {
	return e.Message
}

func newError(tp ErrorType, message string) *Error {
	return &Error{
		Type:    tp,
		Message: message,
	}
}

因此,它们有这个自定义的"Error"类型。在上面的Parse()方法中,错误是通过以下代码块创建的:

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

可以看到newError()返回的是"type *Error"。但是上面的匿名函数返回的是"type error",所以这些类型必须是兼容的。(?)

但现在回到原始问题上-我只是想查看我的"err"是否是"Error"类型,并且成员"Type"是否等于ErrHelp。所以我尝试这样写:

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

或者只是这样写:

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

无论哪种方式,编译器都会给我报错:

main.go:37: 无法将err(类型为error)转换为类型flags.Error

但是没有说明为什么无法进行这种转换。有什么想法吗?

(我不明白为什么"*Error"可以成功转换为"error",而且更不明白为什么如果那样可以工作,我就无法进行反向转换...我肯定是在这里漏掉了一些非常愚蠢的东西,但我没有看到是什么。)

英文:

I'm using <a href="https://github.com/jessevdk/go-flags">go-flags</a> 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(&amp;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 &amp;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(&amp;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 &amp;&amp; flags.Error(err).Type == flags.ErrHelp {

Or even just this:

fmt.Printf(&quot;test:&quot;, 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.)

答案1

得分: 20

一个error是一个带有一个方法Error() string的接口。参见http://golang.org/pkg/builtin/#error

一个flags.Error具有这样的方法,因此它可以被用作一个error

然而,相反地,一个flags.Error是一个结构体,没有办法将任意值转换为结构体。

你可以做的是,我认为这是你问题的答案,如果你有一个flags.Value在一个error中,那么你可以将error转换回底层类型。语法是e := err.(*flags.Error)。这将给你一个类型为*flags.Error的值(如果底层类型不是*flags.Error,则会引发panic)。在这种情况下,你可以使用逗号-ok形式来避免panic,即e, ok := err.(*flags.Error)

具体来说,你可以这样写:

  args, err := flags.Parse()
  if err != nil {
     if ferr, ok := err.(*flags.Error); ok {
       // ... 使用ferr的一些操作
     } else {
       // ... 处理非flags.Error的情况,如果可能的话。
     }
  }
英文:

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&#39;s possible.
     }
  }

huangapple
  • 本文由 发表于 2013年8月24日 15:13:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/18416042.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定