一个空指针被发送到通道,但是接收到的是一个“非空”的值。

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

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 (
	&quot;fmt&quot;
	&quot;os/exec&quot;
)

func main() {
	errChan := make(chan error)
	go func() {
		var e *exec.Error = nil
		errChan &lt;- e
	}()
	err := &lt;-errChan
	if err != nil {
		fmt.Printf(&quot;err != nil, but err = %v\n&quot;, err)
	}
}

The output is weird: err != nil, but err = &lt;nil&gt;
Try it here: http://play.golang.org/p/_iyh0m7O1a

答案1

得分: 15

问题在于作为错误接口传递到通道中的值不是nil,而是指向nilexec.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 &lt;- 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 (
	&quot;fmt&quot;
	&quot;os/exec&quot;
)

func main() {
	errChan := make(chan *exec.Error)
	go func() {
		var e *exec.Error = nil
		errChan &lt;- e
	}()
	err := &lt;-errChan
	if err != nil {
		fmt.Printf(&quot;err != nil, but err = %v\n&quot;, err)
	} else {
	fmt.Printf(&quot;err == nil\n&quot;)
	}
}

http://play.golang.org/p/l6Fq8O0wJw

huangapple
  • 本文由 发表于 2013年10月11日 19:24:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/19316963.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定