Como verificar se há uma estrutura vazia?

110

Eu defino uma estrutura ...

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

Às vezes eu atribuo uma sessão vazia a ele (porque nil não é possível)

session = Session{};

Então, quero verificar, se está vazio:

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

Obviamente, isso não está funcionando. Como faço para escrever isso?

Michael
fonte
4
A sessão {} não é uma sessão "vazia"; ele é inicializado com cada campo sendo o valor zero.
Paul Hankin

Respostas:

177

Você pode usar == para comparar com um literal composto de valor zero porque todos os campos são comparáveis :

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

exemplo de playground

Por causa de uma ambigüidade de análise , os parênteses são necessários em torno do literal composto na condição if.

O uso de ==acima se aplica a structs onde todos os campos são comparáveis . Se a estrutura contém um campo não comparável (fatia, mapa ou função), então os campos devem ser comparados um a um com seus valores zero.

Uma alternativa para comparar o valor inteiro é comparar um campo que deve ser definido como um valor diferente de zero em uma sessão válida. Por exemplo, se o id do jogador deve ser! = "" Em uma sessão válida, use

if session.playerId == "" {
    fmt.Println("is zero value")
}
Muffin Top
fonte
4
@kristen Cancele a referência do ponteiro e compare. Se sessionnão for nulo *Session, use if (Session{} == *session {.
Muffin Top de
3
Portanto, recebo um erro, struct containing []byte cannot be comparedporque, bem, minha estrutura contém uma fatia de byte.
Nevermore
14
@Nevermore A resposta se aplica a uma estrutura com campos comparáveis. Se sua estrutura contém valores não comparáveis ​​como [] byte, você precisa escrever código para testar todos os campos ou usar o pacote reflet, conforme descrito em outra resposta.
Muffin Top
2
Conforme mencionado por @Nevermore, a ==comparação com os campos de fatia falhará. Para comparar essas estruturas, use reflect.DeepEqualou considere algo mais especializado, como discutido aqui: stackoverflow.com/questions/24534072/…
asgaines
"Parsing ambiguity in [if condition]" salvou meu dia, obrigado :) porque quando eu estava tentando em fmt.Println (session == Session {}), ele funciona.
Franva de
37

Aqui estão mais 3 sugestões ou técnicas:

Com um campo adicional

Você pode adicionar um campo adicional para saber se a estrutura foi preenchida ou está vazia. Eu o nomeei intencionalmente readye não emptyporque o valor zero de a boolé false, então se você criar uma nova estrutura como Session{}seu readycampo será automaticamente falsee ele dirá a verdade: que a estrutura ainda não está pronta (está vazia).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

Ao inicializar a estrutura, você deve definir readycomo true. Seu isEmpty()método não é mais necessário (embora você possa criar um se quiser) porque você pode apenas testar o readypróprio campo.

var s Session

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

A significância deste boolcampo adicional aumenta à medida que a estrutura fica maior ou se contém campos que não são comparáveis ​​(por exemplo, fatia mape valores de função).

Usando o valor zero de um campo existente

Isso é semelhante à sugestão anterior, mas usa o valor zero de um campo existente que é considerado inválido quando a estrutura não está vazia. A usabilidade disso depende da implementação.

Por exemplo, se em seu exemplo seu playerIdnão pode estar vazio string "", você pode usá-lo para testar se sua estrutura está vazia assim:

var s Session

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

Neste caso, vale a pena incorporar esta verificação em um isEmpty()método porque esta verificação depende da implementação:

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

E usando:

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

Use o ponteiro para sua estrutura

A segunda sugestão é usar um ponteiro para a sua estrutura: *Session. Os ponteiros podem ter nilvalores, então você pode testá-los:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
icza
fonte
Ótima resposta. Obrigado, icza!
Evgeny Goldin
Resposta incrível! Acho que seguir a última escolha parece bem idiomático.
DeivinsonTejeda
19

Usar reflect.deepEqual também funciona , especialmente quando você tem um mapa dentro da estrutura

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
fonte
2
usar o reflect.DeepEqual é uma solução muito limpa, mas eu me pergunto se leva mais tempo de processamento? Presumo que esteja comparando todos os campos, além disso, você introduz uma nova importação.
quinta
4

Lembre-se de que, com ponteiros para struct, você terá que cancelar a referência da variável e não compará-la com um ponteiro para struct vazio:

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

Verifique este playground .

Também aqui você pode ver que uma estrutura contendo uma propriedade que é uma porção de ponteiros não pode ser comparada da mesma maneira ...

Shadyyx
fonte
0

Como alternativa às outras respostas, é possível fazer isso com uma sintaxe semelhante à que você pretendia originalmente, se fizer isso por meio de uma caseinstrução em vez de if:

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

exemplo de playground

ML
fonte
0

Apenas uma adição rápida, porque eu abordei o mesmo problema hoje:

Com Go 1.13 é possível usar o novo isZero()método:

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

Não testei isso em relação ao desempenho, mas acho que deve ser mais rápido do que comparar via reflect.DeepEqual().

Shibumi
fonte
-1

Talvez algo como isso

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
fonte