преобразование типа golang не работает, как (I) ожидалось

Я использую 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 (
    // 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,
    }
}

Итак, у них есть этот пользовательский тип «Ошибка». И в приведенном выше методе Parse() внутри создается ошибка с блоком кода, подобным этому:

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

Как видите, newError() возвращает "*Error" в качестве типа. Но анонимная функция выше возвращает тип «ошибка», поэтому эти типы должны быть совместимы. (?)

Но теперь вернемся к исходной проблеме - я просто пытаюсь выяснить, является ли моя "ошибка" "ошибкой" и имеет ли член "Тип", равный ErrHelp. Поэтому я пробую это:

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

Или даже просто это:

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

И в любом случае компилятор дает мне:

main.go:37: невозможно преобразовать err (ошибка типа) в тип flags.Error

Но не указывает, почему это преобразование не может быть выполнено. Любые идеи?

(Я не понимаю, как "*Error" успешно преобразуется в "error" в анонимной функции выше, и я еще больше не понимаю, почему, если это работает, я не могу преобразовать его обратно... Я Должно быть, здесь не хватает чего-то действительно глупого, но я не вижу, что это такое.)


person Brad Peabody    schedule 24.08.2013    source источник


Ответы (1)


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). В этом случае вы можете избежать паники, используя форму с запятой, то есть e, ok := err.(*flags.Error).

Конкретно вы бы написали:

  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.
     }
  }
person Paul Hankin    schedule 24.08.2013
comment
Хорошо, понял - спасибо, и это работает! Я не могу найти в документах, что означает этот синтаксис... err.(*flags.Error) означает дать мне указатель на ошибку, но с типом flags.Error? (Я предполагаю, что, как вы сказали, я не могу преобразовать напрямую в Error, но я могу преобразовать в указатель Error - это имело бы смысл.) - person Brad Peabody; 24.08.2013