¿Cómo verificar una estructura vacía?

110

Defino una estructura ...

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

A veces le asigno una sesión vacía (porque nulo no es posible)

session = Session{};

Entonces quiero comprobar, si está vacío:

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

Obviamente, esto no está funcionando. ¿Cómo lo escribo?

Miguel
fuente
4
La sesión {} no es una sesión "vacía"; se inicializa con cada campo siendo el valor cero.
Paul Hankin

Respuestas:

177

Puede usar == para comparar con un literal compuesto de valor cero porque todos los campos son comparables :

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

ejemplo de patio de recreo

Debido a una ambigüedad de análisis , se requieren paréntesis alrededor del literal compuesto en la condición if.

El uso de lo ==anterior se aplica a estructuras donde todos los campos son comparables . Si la estructura contiene un campo no comparable (sector, mapa o función), los campos deben compararse uno por uno con sus valores cero.

Una alternativa a comparar el valor completo es comparar un campo que debe establecerse en un valor distinto de cero en una sesión válida. Por ejemplo, si la identificación del jugador debe ser! = "" En una sesión válida, use

if session.playerId == "" {
    fmt.Println("is zero value")
}
Muffin Top
fuente
4
@kristen Desreferencia del puntero y compare. Si sessiones un valor no nulo *Session, utilice if (Session{} == *session {.
Muffin Top
3
Entonces obtengo un error, struct containing []byte cannot be comparedporque, bueno, mi estructura contiene un segmento de bytes.
Nevermore
14
@Nevermore La respuesta se aplica a una estructura con campos comparables. Si su estructura contiene valores no comparables como [] byte, entonces necesita escribir código para probar todos los campos o usar el paquete reflect como se describe en otra respuesta.
Muffin Top
2
Como lo menciona @Nevermore, la ==comparación con los campos de sector fallará. Para comparar estas estructuras, use reflect.DeepEqualo considere algo más especializado como se discutió aquí: stackoverflow.com/questions/24534072/…
asgaines
"Analizar la ambigüedad en [si la condición]" me salvó el día, gracias :) porque cuando lo estaba probando en fmt.Println (sesión == Sesión {}), funciona.
Franva
37

Aquí hay 3 sugerencias o técnicas más:

Con un campo adicional

Puede agregar un campo adicional para saber si la estructura se ha llenado o está vacía. Lo nombré intencionalmente readyy no emptyporque el valor cero de a boolsea false, por lo que si crea una nueva estructura como Session{}su readycampo será automáticamente falsey le dirá la verdad: que la estructura aún no está lista (está vacía).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

Cuando inicializa la estructura, debe establecerlo readyen true. Su isEmpty()método ya no es necesario (aunque puede crear uno si lo desea) porque puede probar el readycampo en sí.

var s Session

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

La importancia de este boolcampo adicional aumenta a medida que la estructura crece o si contiene campos que no son comparables (p. Ej., mapValores de función y de sector).

Usar el valor cero de un campo existente

Esto es similar a la sugerencia anterior, pero usa el valor cero de un campo existente que se considera inválido cuando la estructura no está vacía. La usabilidad de esto depende de la implementación.

Por ejemplo, si en su ejemplo playerIdno puede ser el vacío string "", puede usarlo para probar si su estructura está vacía de esta manera:

var s Session

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

En este caso, vale la pena incorporar esta verificación en un isEmpty()método porque esta verificación depende de la implementación:

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

Y usándolo:

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

Use el puntero a su estructura

La segunda sugerencia es usar un puntero a la estructura: *Session. Los punteros pueden tener nilvalores, por lo que puede probarlos:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
icza
fuente
Gran respuesta. ¡Gracias, icza!
Evgeny Goldin
¡Respuesta impresionante! Creo que seguir la última opción parece bastante idiomático.
DeivinsonTejeda
19

Usar reflect.deepEqual también funciona , especialmente cuando tiene un mapa dentro de la estructura

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
fuente
2
usando reflect.DeepEqual es una solución muy limpia, pero me pregunto si toma más tiempo de procesamiento. Supongo que está comparando todos los campos, además de que introduce una nueva importación.
jueves
4

Tenga en cuenta que con los punteros a la estructura, tendrá que eliminar la referencia de la variable y no compararla con un puntero a la estructura vacía:

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

Mira este patio de recreo .

También aquí puede ver que una estructura que contiene una propiedad que es una porción de punteros no se puede comparar de la misma manera ...

shadyyx
fuente
0

Como alternativa a las otras respuestas, es posible hacer esto con una sintaxis similar a la que pretendía originalmente si lo hace a través de una casedeclaración en lugar de una if:

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

ejemplo de patio de recreo

ML
fuente
0

Solo una adición rápida, porque abordé el mismo problema hoy:

Con Go 1.13 es posible utilizar el nuevo isZero()método:

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

No probé esto con respecto al rendimiento, pero supongo que debería ser más rápido que comparar a través de reflect.DeepEqual().

Shibumi
fuente
-1

Tal vez algo como esto

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
fuente