英文:
A nil pointer is sent to chan, but a "non-nil" received
问题
package main
import (
"fmt"
"os/exec"
)
func main() {
errChan := make(chan error)
go func() {
var e *exec.Error = nil
errChan <- e
}()
err := <-errChan
if err != nil {
fmt.Printf("err != nil, but err = %v\n", err)
}
}
输出结果很奇怪:err != nil, but err = <nil>
可以在这里尝试:http://play.golang.org/p/_iyh0m7O1a
英文:
package main
import (
"fmt"
"os/exec"
)
func main() {
errChan := make(chan error)
go func() {
var e *exec.Error = nil
errChan <- e
}()
err := <-errChan
if err != nil {
fmt.Printf("err != nil, but err = %v\n", err)
}
}
The output is weird: err != nil, but err = <nil>
Try it here: http://play.golang.org/p/_iyh0m7O1a
答案1
得分: 15
问题在于作为错误接口传递到通道中的值不是nil
,而是指向nil
的exec.Error
指针。
如果你改变代码如下,程序将会正确运行:
go func() {
var e *exec.Error = nil
if e == nil {
errChan <- nil
}
}()
这是解决问题的适当方式,因为报告没有发生错误的惯用方式是传递一个nil
错误接口。
然而,如果你想改变主函数(可能是因为你使用了一个错误地返回设置为nil
的指针的第三方包),你将不得不对特定类型(*exec.Error)进行类型断言,然后检查它是否为nil
,或者使用reflect
包。
使用reflect
检查nil
的示例:
func IsNil(i interface{}) bool {
// 检查是否是真正的`nil`值
if i == nil {
return true
}
v := reflect.ValueOf(i)
switch v.Kind() {
// 只有以下类型可以为`nil`
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return v.IsNil()
}
return false
}
工作示例:http://play.golang.org/p/rpG1PVTwwM
你可以在这里找到相关讨论:https://groups.google.com/forum/#!topic/golang-nuts/QzVDKv7p0Vs
英文:
The problem lies in that the value passed into the channel as a error interface is not nil
, but rather a exec.Error
pointer which points to nil.
The program will behave correctly if you change:
go func() {
var e *exec.Error = nil
if e == nil {
errChan <- nil
}
}()
This is the appropriate way to solve the problem because the idiomatic way to report that no error occurred is by passing a nil error interface.
However, if you want to change the main instead (maybe because you use a third party package which makes the mistake of returning pointers set to nil), you will have to either do a type assertion to the specific type (*exec.Error) and then check if it is nil, or else use the reflect package.
Example using reflect to check for nil:
func IsNil(i interface{}) bool {
// Check if it is an actual nil-value
if i == nil {
return true
}
v := reflect.ValueOf(i)
switch v.Kind() {
// Only the following kinds can be nil
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return v.IsNil()
}
return false
}
Working example: http://play.golang.org/p/rpG1PVTwwM
You can find a discussion about it here: https://groups.google.com/forum/#!topic/golang-nuts/QzVDKv7p0Vs
答案2
得分: 2
注意:这篇文章只是为了更深入地了解一个可能令人困惑的问题。
使用error
类型的通道是发送错误的惯用方式。
另一种解决方法是更改通道的签名,并明确指出它是一个指向错误的通道指针,而不是一个接口错误的通道:
package main
import (
"fmt"
"os/exec"
)
func main() {
errChan := make(chan *exec.Error)
go func() {
var e *exec.Error = nil
errChan <- e
}()
err := <-errChan
if err != nil {
fmt.Printf("err != nil, but err = %v\n", err)
} else {
fmt.Printf("err == nil\n")
}
}
你可以在这里查看代码:http://play.golang.org/p/l6Fq8O0wJw
英文:
> Note: This post is just to have a bit more insight into a potentially confusing problem.
> Using a channel of type error
is the idiomatic way to send errors.
Another way around this would be to change the channel signature and explicitly say that is a channel pointer to error instead of a channel of interface errors:
package main
import (
"fmt"
"os/exec"
)
func main() {
errChan := make(chan *exec.Error)
go func() {
var e *exec.Error = nil
errChan <- e
}()
err := <-errChan
if err != nil {
fmt.Printf("err != nil, but err = %v\n", err)
} else {
fmt.Printf("err == nil\n")
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论