检查Go中的nil和nil接口

检查Go中的nil和nil接口,go,Go,目前我正在使用这个helper函数来检查nil和nil接口 func isNil(a interface{}) bool { defer func() { recover() }() return a == nil || reflect.ValueOf(a).IsNil() } 由于reflect.ValueOf(a).IsNil()如果值的种类不是Chan,Func,Map,Ptr,接口或切片,我会惊慌失措,因此我加入了延迟recover(),以捕捉这些 是否有更好的方法实现此检查?

目前我正在使用这个helper函数来检查nil和nil接口

func isNil(a interface{}) bool {
  defer func() { recover() }()
  return a == nil || reflect.ValueOf(a).IsNil()
}
由于
reflect.ValueOf(a).IsNil()
如果值的种类不是
Chan
Func
Map
Ptr
接口
切片,我会惊慌失措,因此我加入了延迟
recover()
,以捕捉这些


是否有更好的方法实现此检查?它认为应该有一种更直接的方法来做到这一点。

例如,请参见golang nuts邮件列表中Kyle的回答


简而言之:如果您从未在接口中存储
(*T)(nil)
,那么您可以可靠地使用与nil的比较,而无需使用反射。另一方面,将非类型的nil分配给接口总是可以的。

两种解决方案不使用反射:

将代码复制并粘贴到编辑器中:以查看操作

1:向接口添加“IsInterfaceNil()”函数。 2:使用“类型开关” 下面的代码:

一一一一一一一一一一一一一一一一一一一一

//:Example #1:
//:I prefer this method because the 
//:TakesInterface function does NOT need to know
//:about all the different implementations of
//:the interface.
package main;
import "fmt";

func main()(){

    var OBJ_OK *MyStruct = &( MyStruct{} );
    var NOT_OK *MyStruct = nil;
    
    //:Will succeed:
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    TakesInterface( NOT_OK );

}

func TakesInterface( input_arg MyInterface ){

    if( input_arg.IsInterfaceNil() ){
        panic("[InputtedInterfaceIsNil]");
    }
    
    input_arg.DoThing();
}

type MyInterface interface{
    DoThing()()
    IsInterfaceNil()(bool)
}
type MyStruct struct{}
func(f *MyStruct)DoThing()(){
    fmt.Println("[MyStruct.DoThing]");
}
func(f *MyStruct)IsInterfaceNil()(bool){
    if(nil==f){ return true; }
    return false;
}
//:Example #2:
//:This will also work, but the function taking
//:the interface needs to know about all 
//:implementations. This defeats a bit of the
//:decoupling from implementation that an
//:interface offers, but if you are just using
//:interfaces for polymorphism, it's probably
//:an okay way to go. (opinion)
package main;
import "fmt";

func main()(){

    //:Will succeed:
    var OBJ_OK *IMPLMENTS_INTERFACE_01 = 
             &( IMPLMENTS_INTERFACE_01{} );
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    var NOT_OK *IMPLMENTS_INTERFACE_01 = nil;
    TakesInterface( NOT_OK );
}

func TakesInterface( hasDoThing MyInterface ){

    //:THIS WILL NOT WORK:
    if(nil==hasDoThing){
        panic("[This_Error_Message_Will_Never_Happen]");
    }
    
    //:TYPE SWITCH TO THE RESCUE:
    switch v := hasDoThing.(type){
    
        case (*IMPLMENTS_INTERFACE_01): 
        if(nil==v){ panic("[Nil_PTR_01]"); }
        
        case (*IMPLMENTS_INTERFACE_02): 
        if(nil==v){ panic("[Nil_PTR_02]"); }
        
        case (*IMPLMENTS_INTERFACE_03): 
        if(nil==v){ panic("[Nil_PTR_03]"); }
        
        default: 
            panic("[UnsupportedInterface]");
    }
    
    hasDoThing.DoThing();
    
}

type IMPLMENTS_INTERFACE_01 struct{};
type IMPLMENTS_INTERFACE_02 struct{};
type IMPLMENTS_INTERFACE_03 struct{};
func (f *IMPLMENTS_INTERFACE_01)DoThing()(){
    fmt.Println( "DoingTheThing_01" );
}
func (f *IMPLMENTS_INTERFACE_02)DoThing()(){
    fmt.Println( "DoingTheThing_02" );
}
func (f *IMPLMENTS_INTERFACE_03)DoThing()(){
    fmt.Println( "DoingTheThing_03" );
}

type MyInterface interface{
    DoThing()()
}
示例#1:IsInterfaceNil()

一一一一一一一一一一一一一一一一一一一一

//:Example #1:
//:I prefer this method because the 
//:TakesInterface function does NOT need to know
//:about all the different implementations of
//:the interface.
package main;
import "fmt";

func main()(){

    var OBJ_OK *MyStruct = &( MyStruct{} );
    var NOT_OK *MyStruct = nil;
    
    //:Will succeed:
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    TakesInterface( NOT_OK );

}

func TakesInterface( input_arg MyInterface ){

    if( input_arg.IsInterfaceNil() ){
        panic("[InputtedInterfaceIsNil]");
    }
    
    input_arg.DoThing();
}

type MyInterface interface{
    DoThing()()
    IsInterfaceNil()(bool)
}
type MyStruct struct{}
func(f *MyStruct)DoThing()(){
    fmt.Println("[MyStruct.DoThing]");
}
func(f *MyStruct)IsInterfaceNil()(bool){
    if(nil==f){ return true; }
    return false;
}
//:Example #2:
//:This will also work, but the function taking
//:the interface needs to know about all 
//:implementations. This defeats a bit of the
//:decoupling from implementation that an
//:interface offers, but if you are just using
//:interfaces for polymorphism, it's probably
//:an okay way to go. (opinion)
package main;
import "fmt";

func main()(){

    //:Will succeed:
    var OBJ_OK *IMPLMENTS_INTERFACE_01 = 
             &( IMPLMENTS_INTERFACE_01{} );
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    var NOT_OK *IMPLMENTS_INTERFACE_01 = nil;
    TakesInterface( NOT_OK );
}

func TakesInterface( hasDoThing MyInterface ){

    //:THIS WILL NOT WORK:
    if(nil==hasDoThing){
        panic("[This_Error_Message_Will_Never_Happen]");
    }
    
    //:TYPE SWITCH TO THE RESCUE:
    switch v := hasDoThing.(type){
    
        case (*IMPLMENTS_INTERFACE_01): 
        if(nil==v){ panic("[Nil_PTR_01]"); }
        
        case (*IMPLMENTS_INTERFACE_02): 
        if(nil==v){ panic("[Nil_PTR_02]"); }
        
        case (*IMPLMENTS_INTERFACE_03): 
        if(nil==v){ panic("[Nil_PTR_03]"); }
        
        default: 
            panic("[UnsupportedInterface]");
    }
    
    hasDoThing.DoThing();
    
}

type IMPLMENTS_INTERFACE_01 struct{};
type IMPLMENTS_INTERFACE_02 struct{};
type IMPLMENTS_INTERFACE_03 struct{};
func (f *IMPLMENTS_INTERFACE_01)DoThing()(){
    fmt.Println( "DoingTheThing_01" );
}
func (f *IMPLMENTS_INTERFACE_02)DoThing()(){
    fmt.Println( "DoingTheThing_02" );
}
func (f *IMPLMENTS_INTERFACE_03)DoThing()(){
    fmt.Println( "DoingTheThing_03" );
}

type MyInterface interface{
    DoThing()()
}
一一一一一一一一一一一一一一一一一一一一

//:Example #1:
//:I prefer this method because the 
//:TakesInterface function does NOT need to know
//:about all the different implementations of
//:the interface.
package main;
import "fmt";

func main()(){

    var OBJ_OK *MyStruct = &( MyStruct{} );
    var NOT_OK *MyStruct = nil;
    
    //:Will succeed:
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    TakesInterface( NOT_OK );

}

func TakesInterface( input_arg MyInterface ){

    if( input_arg.IsInterfaceNil() ){
        panic("[InputtedInterfaceIsNil]");
    }
    
    input_arg.DoThing();
}

type MyInterface interface{
    DoThing()()
    IsInterfaceNil()(bool)
}
type MyStruct struct{}
func(f *MyStruct)DoThing()(){
    fmt.Println("[MyStruct.DoThing]");
}
func(f *MyStruct)IsInterfaceNil()(bool){
    if(nil==f){ return true; }
    return false;
}
//:Example #2:
//:This will also work, but the function taking
//:the interface needs to know about all 
//:implementations. This defeats a bit of the
//:decoupling from implementation that an
//:interface offers, but if you are just using
//:interfaces for polymorphism, it's probably
//:an okay way to go. (opinion)
package main;
import "fmt";

func main()(){

    //:Will succeed:
    var OBJ_OK *IMPLMENTS_INTERFACE_01 = 
             &( IMPLMENTS_INTERFACE_01{} );
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    var NOT_OK *IMPLMENTS_INTERFACE_01 = nil;
    TakesInterface( NOT_OK );
}

func TakesInterface( hasDoThing MyInterface ){

    //:THIS WILL NOT WORK:
    if(nil==hasDoThing){
        panic("[This_Error_Message_Will_Never_Happen]");
    }
    
    //:TYPE SWITCH TO THE RESCUE:
    switch v := hasDoThing.(type){
    
        case (*IMPLMENTS_INTERFACE_01): 
        if(nil==v){ panic("[Nil_PTR_01]"); }
        
        case (*IMPLMENTS_INTERFACE_02): 
        if(nil==v){ panic("[Nil_PTR_02]"); }
        
        case (*IMPLMENTS_INTERFACE_03): 
        if(nil==v){ panic("[Nil_PTR_03]"); }
        
        default: 
            panic("[UnsupportedInterface]");
    }
    
    hasDoThing.DoThing();
    
}

type IMPLMENTS_INTERFACE_01 struct{};
type IMPLMENTS_INTERFACE_02 struct{};
type IMPLMENTS_INTERFACE_03 struct{};
func (f *IMPLMENTS_INTERFACE_01)DoThing()(){
    fmt.Println( "DoingTheThing_01" );
}
func (f *IMPLMENTS_INTERFACE_02)DoThing()(){
    fmt.Println( "DoingTheThing_02" );
}
func (f *IMPLMENTS_INTERFACE_03)DoThing()(){
    fmt.Println( "DoingTheThing_03" );
}

type MyInterface interface{
    DoThing()()
}
示例#2:类型开关

一一一一一一一一一一一一一一一一一一一一

//:Example #1:
//:I prefer this method because the 
//:TakesInterface function does NOT need to know
//:about all the different implementations of
//:the interface.
package main;
import "fmt";

func main()(){

    var OBJ_OK *MyStruct = &( MyStruct{} );
    var NOT_OK *MyStruct = nil;
    
    //:Will succeed:
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    TakesInterface( NOT_OK );

}

func TakesInterface( input_arg MyInterface ){

    if( input_arg.IsInterfaceNil() ){
        panic("[InputtedInterfaceIsNil]");
    }
    
    input_arg.DoThing();
}

type MyInterface interface{
    DoThing()()
    IsInterfaceNil()(bool)
}
type MyStruct struct{}
func(f *MyStruct)DoThing()(){
    fmt.Println("[MyStruct.DoThing]");
}
func(f *MyStruct)IsInterfaceNil()(bool){
    if(nil==f){ return true; }
    return false;
}
//:Example #2:
//:This will also work, but the function taking
//:the interface needs to know about all 
//:implementations. This defeats a bit of the
//:decoupling from implementation that an
//:interface offers, but if you are just using
//:interfaces for polymorphism, it's probably
//:an okay way to go. (opinion)
package main;
import "fmt";

func main()(){

    //:Will succeed:
    var OBJ_OK *IMPLMENTS_INTERFACE_01 = 
             &( IMPLMENTS_INTERFACE_01{} );
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    var NOT_OK *IMPLMENTS_INTERFACE_01 = nil;
    TakesInterface( NOT_OK );
}

func TakesInterface( hasDoThing MyInterface ){

    //:THIS WILL NOT WORK:
    if(nil==hasDoThing){
        panic("[This_Error_Message_Will_Never_Happen]");
    }
    
    //:TYPE SWITCH TO THE RESCUE:
    switch v := hasDoThing.(type){
    
        case (*IMPLMENTS_INTERFACE_01): 
        if(nil==v){ panic("[Nil_PTR_01]"); }
        
        case (*IMPLMENTS_INTERFACE_02): 
        if(nil==v){ panic("[Nil_PTR_02]"); }
        
        case (*IMPLMENTS_INTERFACE_03): 
        if(nil==v){ panic("[Nil_PTR_03]"); }
        
        default: 
            panic("[UnsupportedInterface]");
    }
    
    hasDoThing.DoThing();
    
}

type IMPLMENTS_INTERFACE_01 struct{};
type IMPLMENTS_INTERFACE_02 struct{};
type IMPLMENTS_INTERFACE_03 struct{};
func (f *IMPLMENTS_INTERFACE_01)DoThing()(){
    fmt.Println( "DoingTheThing_01" );
}
func (f *IMPLMENTS_INTERFACE_02)DoThing()(){
    fmt.Println( "DoingTheThing_02" );
}
func (f *IMPLMENTS_INTERFACE_03)DoThing()(){
    fmt.Println( "DoingTheThing_03" );
}

type MyInterface interface{
    DoThing()()
}
更新: 在我的代码库中实现后,我发现#2(类型开关)是最好的解决方案。特别是因为我不想编辑我正在使用的绑定库中的glfw.Window结构。这是我的用例的粘贴箱。
为我的非标准编码风格道歉

如果前面的两个选项都不适用于您,那么到目前为止,我能想到的最好方法是:

if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil())

至少它检测(*T)(无)情况。

这是此exmaple解决方案的接口定义:

package checker

import (
    "errors"

    "github.com/rs/zerolog"
)

var (
    // ErrNilChecker returned if Check invoked on a nil checker
    ErrNilChecker = errors.New("attempted Check with nil Checker")

    // ErrNilLogger returned if the Check function is provide a nil logger
    ErrNilLogger = errors.New("nil logger provided for Check")
)

// Checker defines the interface
type Checker interface {
    Check(logger *zerolog.Logger) error
}
我们的一个Checker实现支持Checker的聚合。但是测试发现了与此线程相同的问题。 如果简单的零检查失败,此解决方案将使用
reflect
包,利用
reflect.Value
类型来解决问题

// AggregateChecker implements the Checker interface, and
//  supports reporting the results of applying each checker
type AggregateChecker struct {
    checkers []Checker
}

func (ac *AggregateChecker) Add(aChecker Checker) error {
    if aChecker == nil {
        return ErrNilChecker
    }

    // It is possible the interface is a typed nil value
    // E.g. checker := (&MyChecker)(nil)
    t := reflect.TypeOf(aChecker)
    if reflect.ValueOf(aChecker) == reflect.Zero(t) {
        return ErrNilChecker
    }

    ac.checkers = append(ac.checkers, aChecker)
    return nil
}

inboundData
视为您的界面

if inboundData != nil && len(inboundData) > 0 {
    // ... do stuff | data is present
} else {
    // ... data is not present
}
使用
len()
功能检查界面中是否有数据

if inboundData != nil && len(inboundData) > 0 {
    // ... do stuff | data is present
} else {
    // ... data is not present
}

我不明白。。。为什么一个简单的
a==nil
不起作用?@SongGao:OP正在检查两件不同的事情:1)如果
a
是nil接口本身(在这种情况下
a==nil
将为真),或者如果
a
是一个非nil接口,其底层值是通道、函数、指针或片类型的
nil
值(在这种情况下,
a==nil
将为假)被接受的答案是不正确的,我遇到了同样的问题。不幸的是,正确的答案是检查每个案例。那么
[]T(nil)
map[T]T(nil)
func()(nil)
chant(nil)呢
?这看起来像是一个等待发生的恶意bug。很抱歉我的无知,你能进一步解释一下“另一方面,给接口分配非类型的nil总是可以的”的含义吗?我想知道这意味着什么……首先,我不知道nil可以被键入或非键入……我相信它是(nil)任何指针的特殊值types@Victor它们只是表示可以将nil存储在接口变量中。例如:
var thing interface{};thing=nil
。这是完全合法的。
thing
的“类型”为
interface{}
,但没有基础类型。但是,不能将nil存储在非类型化变量中,如
thing:=nil
。nil是“类型化的”因为它需要某种类型的指针。比如nil int指针或nil string指针。如果没有接口,你就不能有一个指向未知类型的nil指针。但是,它是一种类型化的……这确实令人困惑。@Nathalutterman谢谢你的解释,我明白了这个概念。仍然令人困惑,因为nil是一个不是类型的值(至少在golang世界之外…)“untyped nil”的安装我建议使用“literal nil”这个词,因为它是放在表达式中的值本身,而不是放在一个变量、constanst或函数调用返回值中的值。这并不能解决普通接口{}的问题,这就是问题所在。我同意。这是一个解决办法。我对“简言之,[…]从不在接口中存储(*t)(nil)”的公认答案不满意,只要
c
不是指向片的类型指针等。在这种情况下,类似
reflect.Ptr(reflect.ValueOf(c))和&reflect.ValueOf(c.Elem().IsNil()的逻辑
是必需的。我怀疑是否涵盖了所有其他情况……1.)这不适用于
接口{}
。我假设
inboundData
在您的例子中是一个切片或一个映射。2.)它不知道什么时候某个东西是
nil
,什么时候某个贴图或切片是空的。3.)对于地图和切片,如果您只想知道是否有数据,根本不需要执行
nil
-在
len(…)
之前检查。在这种情况下,只需执行
len(…)