101 votes

Vérifier l'existence de nil et de l'interface nil dans Go

Actuellement, j'utilise cette fonction d'aide pour vérifier les interfaces nil et nil.

func isNil(a interface{}) bool {
  defer func() { recover() }()
  return a == nil || reflect.ValueOf(a).IsNil()
}

Depuis reflect.ValueOf(a).IsNil() panique si le Kind de la valeur est autre chose que Chan , Func , Map , Ptr , Interface o Slice j'ai jeté dans le différé recover() pour les attraper.

Existe-t-il un meilleur moyen de réaliser ce contrôle ? Je pense qu'il devrait y avoir un moyen plus direct de le faire.

48voto

zzzz Points 23017

Voir, par exemple, la réponse de Kyle dans cet article. filetage à la liste de diffusion golang-nuts.

En bref : Si vous ne stockez jamais (*T)(nil) dans une interface, alors vous pouvez utiliser de manière fiable la comparaison avec nil, sans avoir besoin d'utiliser la réflexion. D'autre part, l'affectation de nil non typé à une interface est toujours acceptable.

31voto

aaa bbb Points 677

Si aucune des options précédentes ne vous convient, la meilleure solution que j'ai trouvée jusqu'à présent est :

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

Au moins, il détecte les cas (*T)(nil).

10voto

J.M.I. MADISON Points 857

Deux solutions NE PAS utiliser la réflexion :

Copier et coller le code dans l'éditeur à : https://play.golang.org/ à voir en action.

1 : Ajouter une fonction "IsInterfaceNil()" à l'interface.

2 : Utilisez un "interrupteur de type".

CODE CI-DESSOUS :

EXEMPLE #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;
}

EXEMPLE n° 2 : Changement de type

//: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 : Après l'avoir implémenté dans ma base de code, j'ai trouvé que #2 (type switch) était la meilleure solution. Spécifiquement parce que je ne veux pas EDITER la structure glfw.Window dans la bibliothèque de bindings que j'utilise. Voici un paste-bin de mon cas d'utilisation. Je m'excuse pour mon style de codage non standard. https://pastebin.com/22SUDeGG

0voto

Voici la définition de l'interface pour cet exemple de solution :

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
}

Une de nos implémentations de Checker supporte l'agrégation de Checkers. Mais les tests ont révélé le même problème que ce fil de discussion. Cette solution utilise le reflect en cas d'échec de la vérification simple de nil, en tirant parti de la fonction reflect.Value pour résoudre la question.

// 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
}

0voto

win5do Points 1
func main() {
    var foo interface{}
    fmt.Println(reflect.TypeOf(foo) == nil) // true

    type Bar struct{}
    var bar *Bar
    fmt.Println(reflect.TypeOf(bar) != nil) // true
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X