英文:
Go error handling, type assertion, and the net package
问题
我正在学习Go语言,并试图理解如何从通用的错误类型中获取更详细的错误信息。我将使用net包中的DialTimeout函数作为示例。
函数签名如下:
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
error类型只定义了一个Error() string
函数。如果我想要了解DialTimeout失败的具体原因,该如何获取这些信息呢?我发现可以使用类型断言来获取net.Error
特定的错误:
con, err := net.DialTimeout("tcp", net.JoinHostPort(address, "22"),
time.Duration(5) * time.Second)
if err != nil {
netErr, ok := err.(net.Error)
if ok && netErr.Timeout() {
// ...
}
}
但这只告诉我是否发生了超时。例如,假设我想要区分被拒绝的连接和无法到达主机的情况,我该如何做呢?
也许DialTimeout函数过于高级,无法提供这种详细信息,但即使查看syscall.Connect,我也看不到如何获取具体的错误信息。它只是返回通用的错误类型。与此相比,Posix的connect函数会返回各种错误码,让我知道连接失败的原因。
我的一般问题是:如果Go语言文档没有告诉我可能返回哪种类型的错误,我应该如何从通用的error
类型中提取错误详情呢?
英文:
I'm learning go and trying to understand how to get more detailed error information out of the generic error type. The example I'll use is from the net package, specifically the DialTimeout function.
The signature is
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
The error type only defines an Error() string
function. If I want to find out exactly why DialTimeout failed, how can I get that information? I found out that I can use type assertion to get the net.Error
specific error:
con, err := net.DialTimeout("tcp", net.JoinHostPort(address, "22"),
time.Duration(5) * time.Second)
if err != nil {
netErr, ok := err.(net.Error)
if ok && netErr.Timeout() {
// ...
}
}
but that only tells me whether or not I had a timeout. For example, say I wanted to differentiate between a refused connection and no route to host. How can I do that?
Maybe DialTimeout is too high-level to give me that kind of detail, but even looking at syscall.Connect, I don't see how to get the specific error. It just says it returns the generic error type. Compare that to the Posix connect, which will let me know why it failed with the various return codes.
My general question is: how am I supposed to pull out error details from the generic error
type if the golang docs don't tell me what type of errors may be returned?
答案1
得分: 18
大多数网络操作返回一个*OpError
,其中包含有关错误的详细信息并实现了net.Error
接口。因此,在大多数情况下,使用net.Error
就足够了,就像你已经做的那样。
但是对于你的情况,你可能希望断言返回的错误是*net.OpError
,并使用内部错误:
if err != nil {
if oerr, ok := err.(*OpError); ok {
// 使用oerr.Err做一些操作
}
}
一旦你这样做,你就处于平台依赖的领域,因为在Linux下的系统调用可能与Windows下的系统调用失败方式不同。对于Linux,你可以这样做:
if oerr.Err == syscall.ECONNREFUSED {
// 连接被拒绝 :(
}
syscall
包包含了平台的重要错误常量。不幸的是,golang网站只显示了Linux amd64的syscall包。请参阅这里获取ECONNREFUSED
的信息。
查找类型
下次当你想知道某个函数实际返回的是什么,而你又无法理解它时,可以尝试使用fmt.Printf
(和相关函数)中指定的%#v
格式:
fmt.Printf("%#v\n", err)
// &net.OpError{Op:"dial", Net:"tcp", Addr:(*net.TCPAddr)(0xc20006d390), Err:0x6f}
它会打印详细的类型信息,通常非常有帮助。
英文:
Most networking operations return a *OpError
which holds detailed information about the error
and implements the net.Error
interface. So for most use cases it is sufficient to use net.Error
as you already did.
But for your case you'd want to assert the returned error to be *net.OpError
and
use the internal error:
if err != nil {
if oerr, ok := err.(*OpError); ok {
// Do something with oerr.Err
}
}
As soon as you're doing this you are in the land of platform dependency as syscalls under Linux
can fail differently to those under Windows. For Linux you'd do something like this:
if oerr.Err == syscall.ECONNREFUSED {
// Connection was refused :(
}
The syscall
package contains the important error constants for your platform. Unfortunately
the golang website only shows the syscall package for Linux amd64. See here for ECONNREFUSED
.
Finding out types
The next time you're wondering what is actually returned by some function and you can't make
heads and tails of it, try using the %#v
format specified in fmt.Printf
(and friends):
fmt.Printf("%#v\n", err)
// &net.OpError{Op:"dial", Net:"tcp", Addr:(*net.TCPAddr)(0xc20006d390), Err:0x6f}
It will print detailed type information and is generally quite helpful.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论