Wie suche ich nach einer leeren Struktur?

110

Ich definiere eine Struktur ...

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

Manchmal weise ich ihm eine leere Sitzung zu (weil Null nicht möglich ist)

session = Session{};

Dann möchte ich überprüfen, ob es leer ist:

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

Offensichtlich funktioniert das nicht. Wie schreibe ich es?

Michael
quelle
4
Sitzung {} ist keine "leere" Sitzung. Es wird initialisiert, wobei jedes Feld der Nullwert ist.
Paul Hankin

Antworten:

177

Sie können == verwenden, um mit einem zusammengesetzten Nullwert-Literal zu vergleichen, da alle Felder vergleichbar sind :

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

Spielplatz Beispiel

Aufgrund einer Parsing-Mehrdeutigkeit sind in der if-Bedingung Klammern um das zusammengesetzte Literal erforderlich.

Die Verwendung von ==oben gilt für Strukturen, bei denen alle Felder vergleichbar sind . Wenn die Struktur ein nicht vergleichbares Feld (Slice, Map oder Funktion) enthält, müssen die Felder einzeln mit ihren Nullwerten verglichen werden.

Eine Alternative zum Vergleichen des gesamten Werts besteht darin, ein Feld zu vergleichen, das in einer gültigen Sitzung auf einen Wert ungleich Null gesetzt werden muss. Wenn beispielsweise die Spieler-ID in einer gültigen Sitzung! = "" Sein muss, verwenden Sie

if session.playerId == "" {
    fmt.Println("is zero value")
}
Muffin Top
quelle
4
@kristen Dereferenzieren Sie den Zeiger und vergleichen Sie. Wenn sessiones kein Null ist *Session, verwenden Sie if (Session{} == *session {.
Muffin Top
3
Ich erhalte also eine Fehlermeldung, struct containing []byte cannot be comparedweil meine Struktur ein Byte-Slice enthält.
Nie mehr
14
@Nevermore Die Antwort gilt für eine Struktur mit vergleichbaren Feldern. Wenn Ihre Struktur nicht vergleichbare Werte wie [] Byte enthält, müssen Sie Code schreiben, um alle Felder zu testen, oder das Reflect-Paket verwenden, wie in einer anderen Antwort beschrieben.
Muffin Top
2
Wie von @Nevermore erwähnt, schlägt der ==Vergleich mit Slice-Feldern fehl. Verwenden Sie zum Vergleichen dieser Strukturen entweder reflect.DeepEqualetwas oder betrachten Sie etwas Spezielleres, wie hier beschrieben: stackoverflow.com/questions/24534072/…
asgaines
"Parsing Ambiguität in [wenn Bedingung]" hat meinen Tag gerettet, danke :) Weil ich es in fmt.Println (session == Session {}) versucht habe, funktioniert es.
Franva
37

Hier sind 3 weitere Vorschläge oder Techniken:

Mit einem zusätzlichen Feld

Sie können ein zusätzliches Feld hinzufügen, um festzustellen, ob die Struktur ausgefüllt wurde oder leer ist. Ich habe es absichtlich benannt readyund nicht, emptyweil der Nullwert von a boolist false. Wenn Sie also eine neue Struktur wie Session{}das readyFeld erstellen, wird diese automatisch angezeigt falseund es wird Ihnen die Wahrheit sagen: dass die Struktur noch nicht fertig ist (sie ist leer).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

Wenn Sie die Struktur zu initialisieren, müssen Sie Satz readyan true. Ihre isEmpty()Methode wird nicht mehr benötigt (obwohl Sie eine erstellen können, wenn Sie möchten), da Sie nur das readyFeld selbst testen können.

var s Session

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

Die Bedeutung dieses einen zusätzlichen boolFeldes nimmt zu, wenn die Struktur größer wird oder wenn sie Felder enthält, die nicht vergleichbar sind (z. B. Slice- mapund Funktionswerte).

Verwenden des Nullwerts eines vorhandenen Feldes

Dies ähnelt dem vorherigen Vorschlag, verwendet jedoch den Nullwert eines vorhandenen Felds, das als ungültig betrachtet wird wenn die Struktur nicht leer ist. Die Verwendbarkeit hängt von der Implementierung ab.

Wenn Sie in Ihrem Beispiel beispielsweise playerIdnicht leer sein können string "", können Sie damit testen, ob Ihre Struktur wie folgt leer ist:

var s Session

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

In diesem Fall lohnt es sich, diese Prüfung in eine isEmpty()Methode aufzunehmen, da diese Prüfung implementierungsabhängig ist:

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

Und damit:

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

Verwenden Sie den Zeiger für Ihre Struktur

Der zweite Vorschlag ist, einen Zeiger auf Ihre Struktur zu verwenden : *Session. Zeiger können nilWerte haben, sodass Sie sie testen können:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
icza
quelle
Gute Antwort. Danke, icza!
Evgeny Goldin
Super Antwort! Ich denke, der letzten Wahl zu folgen, sieht ziemlich idiomatisch aus.
DeivinsonTejeda
19

Mit reflect.deepEqual auch funktioniert , vor allem , wenn Sie Karte innerhalb der Struktur haben

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
quelle
2
Verwenden von Reflect.DeepEqual ist eine sehr saubere Lösung, aber ich frage mich, ob es mehr Verarbeitungszeit dauert? Ich gehe davon aus, dass jedes Feld verglichen wird und Sie einen neuen Import einführen.
Thurt
4

Beachten Sie, dass Sie bei Zeigern auf struct die Variable dereferenzieren und nicht mit einem Zeiger auf leere struct vergleichen müssen:

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

Überprüfen Sie diesen Spielplatz .

Auch hier können Sie sehen, dass eine Struktur, die eine Eigenschaft enthält, die ein Teil von Zeigern ist, nicht auf die gleiche Weise verglichen werden kann ...

Shadyyx
quelle
0

Alternativ zu den anderen Antworten ist es möglich, dies mit einer Syntax zu tun, die der ursprünglich beabsichtigten ähnelt, wenn Sie dies über eine caseAnweisung und nicht über Folgendes tun if:

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

Spielplatz Beispiel

ML
quelle
0

Nur eine kurze Ergänzung, denn ich habe heute das gleiche Problem angegangen:

Mit Go 1.13 ist es möglich, die neue isZero()Methode zu verwenden:

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

Ich habe dies nicht in Bezug auf die Leistung getestet, aber ich denke, dass dies schneller sein sollte als ein Vergleich über reflect.DeepEqual().

Shibumi
quelle
-1

Vielleicht so etwas wie diese

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
quelle