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

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

golang type conversion not working as (I) expected

问题

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

根据go-flags文档:

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

我调用的方法是:

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

我这样调用它:

  1. var opts struct {
  2. // ...
  3. }
  4. func main() {
  5. parser := flags.NewParser(&opts, flags.Default)
  6. args, err := parser.Parse()

定义ErrHelp的文件片段如下:

  1. type ErrorType uint
  2. const (
  3. // 未知或通用错误
  4. ErrUnknown ErrorType = iota
  5. // 预期需要参数但未提供
  6. ErrExpectedArgument
  7. // ...
  8. // 错误包含内置的帮助信息
  9. ErrHelp
  10. // ...
  11. )
  12. // Error表示解析器的错误。Parse返回的错误属于此类型。错误包含类型和消息。
  13. type Error struct {
  14. // 错误类型
  15. Type ErrorType
  16. // 错误消息
  17. Message string
  18. }
  19. // 获取错误消息。
  20. func (e *Error) Error() string {
  21. return e.Message
  22. }
  23. func newError(tp ErrorType, message string) *Error {
  24. return &Error{
  25. Type: tp,
  26. Message: message,
  27. }
  28. }

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

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

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

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

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

或者只是这样写:

  1. 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:

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

I'm calling it with:

  1. var opts struct {
  2. // ...
  3. }
  4. func main() {
  5. parser := flags.NewParser(&amp;opts, flags.Default)
  6. args, err := parser.Parse()

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

  1. type ErrorType uint
  2. const (
  3. // Unknown or generic error
  4. ErrUnknown ErrorType = iota
  5. // Expected an argument but got none
  6. ErrExpectedArgument
  7. // ...
  8. // The error contains the builtin help message
  9. ErrHelp
  10. // ...
  11. )
  12. // Error represents a parser error. The error returned from Parse is of this
  13. // type. The error contains both a Type and Message.
  14. type Error struct {
  15. // The type of error
  16. Type ErrorType
  17. // The error message
  18. Message string
  19. }
  20. // Get the errors error message.
  21. func (e *Error) Error() string {
  22. return e.Message
  23. }
  24. func newError(tp ErrorType, message string) *Error {
  25. return &amp;Error{
  26. Type: tp,
  27. Message: message,
  28. }
  29. }

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:

  1. help.ShowHelp = func() error {
  2. var b bytes.Buffer
  3. p.WriteHelp(&amp;b)
  4. return newError(ErrHelp, b.String())
  5. }

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:

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

Or even just this:

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

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

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

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:

  1. args, err := flags.Parse()
  2. if err != nil {
  3. if ferr, ok := err.(*flags.Error); ok {
  4. // ... something using ferr
  5. } else {
  6. // ... deal with non-flags.Error case, if that&#39;s possible.
  7. }
  8. }

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:

确定