Comment vérifier une structure vide?

110

Je définis une structure ...

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

Parfois, je lui assigne une session vide (car nul n'est possible)

session = Session{};

Ensuite, je veux vérifier, s'il est vide:

if session == Session{} {
     // do stuff...
}

De toute évidence, cela ne fonctionne pas. Comment l'écrire?

Michael
la source
4
La session {} n'est pas une session "vide"; il est initialisé avec chaque champ étant la valeur zéro.
Paul Hankin le

Réponses:

177

Vous pouvez utiliser == pour comparer avec un littéral composite de valeur zéro car tous les champs sont comparables :

if (Session{}) == session  {
    fmt.Println("is zero value")
}

exemple de terrain de jeu

En raison d'une ambiguïté d'analyse , des parenthèses sont requises autour du littéral composite dans la condition if.

L'utilisation de ==ci - dessus s'applique aux structures où tous les champs sont comparables . Si la structure contient un champ non comparable (tranche, carte ou fonction), alors les champs doivent être comparés un par un à leurs valeurs nulles.

Une alternative à la comparaison de la valeur entière consiste à comparer un champ qui doit être défini sur une valeur différente de zéro dans une session valide. Par exemple, si l'ID du joueur doit être! = "" Dans une session valide, utilisez

if session.playerId == "" {
    fmt.Println("is zero value")
}
Dessus de muffin
la source
4
@kristen Déréférencer le pointeur et comparer. Si sessionest non nul *Session, alors utilisez if (Session{} == *session {.
Muffin Top du
3
Donc j'obtiens une erreur, struct containing []byte cannot be comparedparce que, eh bien, ma structure contient une tranche d'octets.
Nevermore
14
@Nevermore La réponse s'applique à une structure avec des champs comparables. Si votre structure contient des valeurs non comparables comme [] byte, alors vous devez écrire du code pour tester tous les champs ou utiliser le package refléter comme indiqué dans une autre réponse.
Muffin Top
2
Comme mentionné par @Nevermore, la ==comparaison avec les champs de tranche échouera. Pour comparer ces structures, utilisez l'un reflect.DeepEqualou l' autre ou envisagez quelque chose de plus spécialisé comme discuté ici: stackoverflow.com/questions/24534072/…
asgaines
"analyser l'ambiguïté dans [si condition]" m'a sauvé la journée, merci :) car quand je l'ai essayé dans fmt.Println (session == Session {}), cela fonctionne.
Franva
37

Voici 3 autres suggestions ou techniques:

Avec un champ supplémentaire

Vous pouvez ajouter un champ supplémentaire pour dire si la structure a été remplie ou si elle est vide. Je l'ai nommé intentionnellement readyet pas emptyparce que la valeur zéro de a boolest false, donc si vous créez une nouvelle structure comme Session{}son readychamp le sera automatiquement falseet elle vous dira la vérité: que la structure n'est pas encore prête (elle est vide).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

Lorsque vous initialisez la structure, vous devez définir readysur true. Votre isEmpty()méthode n'est plus nécessaire (bien que vous puissiez en créer une si vous le souhaitez) car vous pouvez simplement tester le readychamp lui-même.

var s Session

if !s.ready {
    // do stuff (populate s)
}

La signification de ce boolchamp supplémentaire augmente au fur et à mesure que la structure grossit ou si elle contient des champs qui ne sont pas comparables (par exemple, maples valeurs de tranche et de fonction).

Utilisation de la valeur zéro d'un champ existant

Ceci est similaire à la suggestion précédente, mais il utilise la valeur zéro d'un champ existant qui est considéré comme invalide lorsque la structure n'est pas vide. L'utilisabilité de ceci dépend de l'implémentation.

Par exemple, si dans votre exemple vous playerIdne pouvez pas être le vide string "", vous pouvez l'utiliser pour tester si votre structure est vide comme ceci:

var s Session

if s.playerId == "" {
    // do stuff (populate s, give proper value to playerId)
}

Dans ce cas, il vaut la peine d'incorporer cette vérification dans une isEmpty()méthode car cette vérification dépend de l'implémentation:

func (s Session) isEmpty() bool {
    return s.playerId == ""
}

Et en l'utilisant:

if s.isEmpty() {
    // do stuff (populate s, give proper value to playerId)
}

Utilisez Pointer pour votre structure

La deuxième suggestion est d'utiliser un pointeur sur votre struct: *Session. Les pointeurs peuvent avoir des nilvaleurs, vous pouvez donc le tester:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
icza
la source
Très bonne réponse. Merci, icza!
Evgeny Goldin le
Réponse géniale! Je pense que suivre le dernier choix semble assez idiomatique.
DeivinsonTejeda
19

L'utilisation de reflect.deepEqual fonctionne également , en particulier lorsque vous avez une carte à l'intérieur de la structure

package main

import "fmt"
import "time"
import "reflect"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) IsEmpty() bool {
  return reflect.DeepEqual(s,Session{})
}

func main() {
  x := Session{}
  if x.IsEmpty() {
    fmt.Print("is empty")
  } 
}
Kokizzu
la source
2
utiliser Reflect.DeepEqual est une solution très propre mais je me demande si cela prend plus de temps de traitement? Je suppose qu'il compare chaque champ, plus vous introduisez une nouvelle importation.
jeudi
4

Gardez à l'esprit qu'avec des pointeurs vers struct, vous devez déréférencer la variable et ne pas la comparer avec un pointeur vers une structure vide:

session := &Session{}
if (Session{}) == *session {
    fmt.Println("session is empty")
}

Vérifiez ce terrain de jeu .

Ici aussi, vous pouvez voir qu'une structure contenant une propriété qui est une tranche de pointeurs ne peut pas être comparée de la même manière ...

shadyyx
la source
0

Comme alternative aux autres réponses, il est possible de le faire avec une syntaxe similaire à celle que vous aviez initialement prévue si vous le faites via une caseinstruction plutôt qu'un if:

session := Session{}
switch {
case Session{} == session:
    fmt.Println("zero")
default:
    fmt.Println("not zero")
}

exemple de terrain de jeu

ML
la source
0

Juste un ajout rapide, car j'ai abordé le même problème aujourd'hui:

Avec Go 1.13, il est possible d'utiliser la nouvelle isZero()méthode:

if reflect.ValueOf(session).IsZero() {
     // do stuff...
}

Je n'ai pas testé cela concernant les performances, mais je suppose que cela devrait être plus rapide que de comparer via reflect.DeepEqual().

Shibumi
la source
-1

Peut-être quelque chose comme ça

package main

import "fmt"
import "time"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) Equal(o Session) bool {
   if(s.playerId != o.playerId) { return false }
   if(s.beehive != o.beehive) { return false }
   if(s.timestamp != o.timestamp) { return false }
   return true
}

func (s Session) IsEmpty() bool {
    return s.Equal(Session{})
}

func main() {
    x := Session{}
    if x.IsEmpty() {
       fmt.Print("is empty")
    } 
}
Kokizzu
la source