Keine Erkennung in Go


165

Ich sehe viel Code in Go, um Null zu erkennen, wie folgt:

if err != nil { 
    // handle the error    
}

Ich habe jedoch eine Struktur wie diese:

type Config struct {
    host string  
    port float64
}

und config ist eine Instanz von Config, wenn ich Folgendes tue:

if config == nil {
}

Es liegt ein Kompilierungsfehler vor, der besagt, dass nil nicht in den Typ Config konvertiert werden kann


3
Ich verstehe nicht, warum Port vom Typ float64 ist?
Alamin

2
Es sollte nicht sein. Die JSON-API von Go importiert eine beliebige Zahl von JSON in float64. Ich muss float64 in int konvertieren.
Qian Chen

Antworten:


179

Der Compiler zeigt den Fehler auf Sie, Sie vergleichen eine Strukturinstanz mit nil. Sie sind nicht vom gleichen Typ, daher wird dies als ungültiger Vergleich angesehen und Sie angeschrien.

Was Sie hier tun möchten, ist, einen Zeiger auf Ihre Konfigurationsinstanz mit nil zu vergleichen, was ein gültiger Vergleich ist. Dazu müssen Sie entweder die golang können neue builtin oder initialisieren einen Zeiger darauf:

config := new(Config) // not nil

oder

config := &Config{
                  host: "myhost.com", 
                  port: 22,
                 } // not nil

oder

var config *Config // nil

Dann können Sie überprüfen, ob

if config == nil {
    // then
}

5
Ich denke var config &Config // nilsollte sein:var config *Config
Tomasz Plonka

var config *Configstürzt ab mit invalid memory address or nil pointer dereference. Vielleicht brauchen wirvar config Config
Kachar

Ich verstehe, dass die Gründe für diese Wahl möglicherweise nicht Ihre sind, aber es macht für mich keinen Sinn, dass "if! (Config! = Nil)" gültig ist, "if config == nil" jedoch nicht. Beide führen einen Vergleich zwischen derselben Struktur und Nichtstruktur durch.
Retorquere

@retorquere beide sind ungültig, siehe play.golang.org/p/k2EmRcels6 . Ob es '! =' Oder '==' ist, macht keinen Unterschied; Was einen Unterschied macht, ist, ob config eine Struktur oder ein Zeiger auf struct ist.
Stewbasic

Ich denke, das ist falsch, da es immer falsch ist: play.golang.org/p/g-MdbEbnyNx
Madeo

61

Siehe zusätzlich zu Oleiade die Spezifikation für Nullwerte :

Wenn Speicher zum Speichern eines Werts zugewiesen wird, entweder durch eine Deklaration oder einen Aufruf von make oder new, und keine explizite Initialisierung bereitgestellt wird, erhält der Speicher eine Standardinitialisierung. Jedes Element eines solchen Werts wird für seinen Typ auf den Wert Null gesetzt: false für Boolesche Werte, 0 für Ganzzahlen, 0.0 für Floats, "" für Zeichenfolgen und Null für Zeiger, Funktionen, Schnittstellen, Slices, Kanäle und Maps. Diese Initialisierung erfolgt rekursiv, sodass beispielsweise für jedes Element eines Arrays von Strukturen die Felder auf Null gesetzt werden, wenn kein Wert angegeben wird.

Wie Sie sehen können, nilist dies nicht der Nullwert für jeden Typ, sondern nur für Zeiger, Funktionen, Schnittstellen, Slices, Kanäle und Karten. Dies ist der Grund, warum config == nilein Fehler vorliegt und &config == nilnicht.

Um zu überprüfen , ob Ihre Struktur nicht initialisiert ist Sie müssen jedes Mitglied überprüfen würde für seine jeweiligen Nullwert (zB host == "", port == 0usw.) oder ein privates Feld haben , die durch eine interne Initialisierung Verfahren eingestellt wird. Beispiel:

type Config struct {
    Host string  
    Port float64
    setup bool
}

func NewConfig(host string, port float64) *Config {
    return &Config{host, port, true}
}

func (c *Config) Initialized() bool { return c != nil && c.setup }

4
Darüber hinaus time.Timehat deshalb eine IsZero()Methode. Allerdings könnten Sie auch tun , var t1 time.Time; if t1 == time.Time{}und Sie könnten auch tun if config == Config{}für Sie alle , das Feld zu überprüfen (struct Gleichheit ist gut definiert in Go). Dies ist jedoch nicht effizient, wenn Sie viele Felder haben. Und vielleicht ist der Nullwert ein vernünftiger und verwendbarer Wert, daher ist es nichts Besonderes, einen Wert einzugeben.
Dave C

1
Die initialisierte Funktion schlägt fehl, wenn auf Config als Zeiger zugegriffen wird. Es könnte geändert werden zufunc (c *Config) Initialized() bool { return !(c == nil) }
Sundar

@ Sundar in diesem Fall könnte es bequem sein, dies auf diese Weise zu tun, also habe ich die Änderung angewendet. Normalerweise würde ich jedoch nicht erwarten, dass das empfangende Ende des Methodenaufrufs prüft, ob es selbst Null ist, da dies die Aufgabe des Aufrufers sein sollte.
Nemo

16

Ich habe einen Beispielcode erstellt, der neue Variablen auf verschiedene Arten erstellt, die ich mir vorstellen kann. Es sieht so aus, als würden die ersten drei Methoden Werte erstellen und die letzten beiden Referenzen.

package main

import "fmt"

type Config struct {
    host string
    port float64
}

func main() {
    //value
    var c1 Config
    c2 := Config{}
    c3 := *new(Config)

    //reference
    c4 := &Config{}
    c5 := new(Config)

    fmt.Println(&c1 == nil)
    fmt.Println(&c2 == nil)
    fmt.Println(&c3 == nil)
    fmt.Println(c4 == nil)
    fmt.Println(c5 == nil)

    fmt.Println(c1, c2, c3, c4, c5)
}

welche Ausgänge:

false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}

6

Sie können auch gerne überprüfen struct_var == (struct{}). Auf diese Weise können Sie nicht mit nil vergleichen, es wird jedoch geprüft, ob es initialisiert ist oder nicht. Seien Sie vorsichtig, wenn Sie diese Methode anwenden. Wenn Ihre Struktur für alle Felder Nullwerte haben kann, haben Sie keine gute Zeit.

package main

import "fmt"

type A struct {
    Name string
}

func main() {
    a := A{"Hello"}
    var b A

    if a == (A{}) {
        fmt.Println("A is empty") // Does not print
    } 

    if b == (A{}) {
        fmt.Println("B is empty") // Prints
    } 
}

http://play.golang.org/p/RXcE06chxE


3

In der Sprachspezifikation wird das Verhalten von Vergleichsoperatoren erwähnt:

Vergleichsoperatoren

In jedem Vergleich muss der erste Operand dem Typ des zweiten Operanden zugeordnet werden können oder umgekehrt.


Zuweisbarkeit

Ein Wert x kann in einem der folgenden Fälle einer Variablen vom Typ T zugewiesen werden ("x kann T zugewiesen werden"):

  • Der Typ von x ist identisch mit T.
  • xs Typ V und T haben identische zugrunde liegende Typen und mindestens einer von V oder T ist kein benannter Typ.
  • T ist ein Schnittstellentyp und x implementiert T.
  • x ist ein bidirektionaler Kanalwert, T ist ein Kanaltyp, xs Typ V und T haben identische Elementtypen und mindestens einer von V oder T ist kein benannter Typ.
  • x ist der vordeklarierte Bezeichner nil und T ist ein Zeiger-, Funktions-, Slice-, Map-, Kanal- oder Schnittstellentyp.
  • x ist eine untypisierte Konstante, die durch einen Wert vom Typ T dargestellt werden kann.

0

In Go 1.13 und höher können Sie Value.IsZerodie im reflectPaket angebotene Methode verwenden .

if reflect.ValueOf(v).IsZero() {
    // v is zero, do something
}

Abgesehen von den Basistypen funktioniert es auch für Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer und Struct. Siehe dies als Referenz.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.