Check for nil and nil interface in Go

后端 未结 4 1297
生来不讨喜
生来不讨喜 2020-12-07 18:48

Currently I\'m using this helper function to check for nil and nil interfaces

func isNil(a interface{}) bool {
  defer func() { recover() }()
  return a == n         


        
4条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-07 18:53

    Two solutions NOT using reflection:

    Copy and paste code into editor at: https://play.golang.org/ to see in action.

    1: Add an "IsInterfaceNil()" function to interface.

    2: Use A "type switch"

    CODE BELOW:

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

    EXAMPLE #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: Type Switch

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

    //: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()()
    }
    

    UPDATE: After implementing in my code base, I found #2 (type switch) to be best solution. Specifically because I DON'T want to EDIT the glfw.Window struct in the bindings library I am using. Here is a paste-bin of my use-case. Apologies for my non-standard coding style. https://pastebin.com/22SUDeGG

提交回复
热议问题