英文:
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(&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.)
答案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's possible.
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论