Go'da sıfır algılama


165

Go tespit nil, bir sürü kod görüyorum, şöyle:

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

Ancak, böyle bir yapı var:

type Config struct {
    host string  
    port float64
}

ve config, yaptığım zaman Config'in bir örneğidir:

if config == nil {
}

derleme hatası, diyerek: nil Config türüne dönüştürülemiyor


3
Bağlantı noktasının neden float64 türünde olduğunu anlamıyorum?
alamin

2
Olmamalı. Go JSON api JSON float64 içine herhangi bir sayı ithal, float64 int dönüştürmek zorunda.
Qian Chen

Yanıtlar:


179

Derleyici hatayı size işaret ediyor, bir yapı örneği ve nil karşılaştırıyorsunuz. Aynı türden değildirler, bu yüzden onu geçersiz bir karşılaştırma olarak görür ve size bağırır.

Burada yapmak istediğiniz şey, işaretçiyi yapılandırma örneğinizle nil ile karşılaştırmaktır. Bu, geçerli bir karşılaştırmadır. Bunu yapmak için golang new builtin'i kullanabilir veya ona bir işaretçi başlatabilirsiniz :

config := new(Config) // not nil

veya

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

veya

var config *Config // nil

Ardından,

if config == nil {
    // then
}

5
Sanırım şöyle var config &Config // nilolmalı:var config *Config
Tomasz Plonka

var config *Configile çöküyor invalid memory address or nil pointer dereference. Belki ihtiyacımız varvar config Config
kachar

Ben bu seçimin arkasındaki mantık senin olmayabilir anlıyorum, ama yapar hayır "ise! (Yapılandırma! = Nil)" geçerli olduğunu bana mantıklı ama "yapılandırma == nil eğer" olmadığını. Her ikisi de aynı yapı ile yapı dışı arasında bir karşılaştırma yapıyor.
retorquere

@retorquere ikisi de geçersiz, bkz. play.golang.org/p/k2EmRcels6 . '! =' Veya '==' olsun fark etmez; fark yaratan şey, config'in bir yapı mı yoksa yapı işaretçisi mi olduğudur.
stewbasic

Her zaman yanlış olduğu için bunun yanlış olduğunu düşünüyorum: play.golang.org/p/g-MdbEbnyNx
Madeo

61

Oleiade'ye ek olarak , sıfır değerler hakkındaki spesifikasyona bakın :

Bellek bir değeri saklamak için bir bildirim veya marka oluşturma veya yeni çağrı ile ayrıldığında ve açık başlatma yapılmadığında, belleğe varsayılan başlatma verilir. Böyle bir değerin her elemanı, türü için sıfır değerine ayarlanır: booleans için false, tamsayılar için 0, kayanlar için 0.0, dizeler için "" ve işaretçiler, işlevler, arabirimler, dilimler, kanallar ve haritalar için sıfır. Bu başlatma yinelemeli olarak yapılır, bu nedenle örneğin bir yapı dizisinin her bir öğesinin herhangi bir değer belirtilmezse alanları sıfırlanır.

Gördüğünüz gibi, nilher tür için sıfır değeri değil, yalnızca işaretçiler, işlevler, arabirimler, dilimler, kanallar ve haritalar için. Bu config == nilbir hatadır ve &config == nilolmamasının sebebidir .

Senin yapı (örneğin onun tekabül eden sıfır değeri için her üyeyi kontrol etmek olurdu başlatılmamış olup olmadığını kontrol etmek için host == "", port == 0vs.) ya da bir iç başlatma yöntemi ile ayarlanır özel alan vardır. Misal:

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
Yukarıdakilere ek olarak, bu yüzden time.Timebir IsZero()yöntemi vardır. Ancak aynı zamanda yapabileceği var t1 time.Time; if t1 == time.Time{}ve olabilir de do if config == Config{}(struct eşitliği de git tanımlanmıştır) sizin için tüm alanını kontrol etmek. Ancak, çok fazla alanınız varsa bu verimli değildir. Ve belki de sıfır değeri aklı başında ve kullanılabilir bir değerdir, bu yüzden bir tanesini geçmek özel değildir.
Dave C

1
İşaretçi olarak Yapılandır'a erişilirse, Başlatıldı işlevi başarısız olur. func (c *Config) Initialized() bool { return !(c == nil) }
Şununla

@Sundar bu durumda bu şekilde yapmak uygun olabilir, bu yüzden değişikliği uyguladım. Ancak, normalde yöntem çağrısının alıcı sonunun kendisinin sıfır olup olmadığını kontrol etmesini beklemezdim, çünkü bu arayanın işi olmalıdır.
nemo

16

Aklıma gelen çeşitli yollar kullanarak yeni değişkenler oluşturan bazı örnek kod oluşturduk. İlk 3 yolun değer yarattığı ve son iki yolun da referans oluşturduğu anlaşılıyor.

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)
}

hangi çıktılar:

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

6

Ayrıca kontrol edebilirsiniz struct_var == (struct{}). Bu, nil ile karşılaştırmanıza izin vermez, ancak başlatılıp başlatılmadığını kontrol eder. Bu yöntemi kullanırken dikkatli olun. Yapınız tüm alanları için sıfır değere sahipse, harika zamanınız olmaz.

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

Dil spec karşılaştırma operatörlerinin davranışlarını bahseder:

karşılaştırma operatörleri

Herhangi bir karşılaştırmada, ilk işlenen ikinci işlenenin türüne atanabilir veya tam tersi olmalıdır.


DEVRETME

Aşağıdaki durumlarda T değeri ("x T'ye atanabilir") değişkenine x değeri atanabilir:

  • x'in türü T ile aynıdır.
  • x'in V ve T tipi aynı altta yatan tiplere sahiptir ve V veya T'den en az biri adlandırılmış bir tip değildir.
  • T bir arayüz tipidir ve x T'yi uygular.
  • x çift yönlü bir kanal değeridir, T bir kanal tipidir, x'in V tipi ve T aynı eleman tipine sahiptir ve V veya T'den en az biri adlandırılmış tip değildir.
  • x, önceden tanımlanmış tanımlayıcı nildir ve T, bir işaretçi, işlev, dilim, harita, kanal veya arabirim türüdür.
  • x, T tipi bir değerle temsil edilebilen türlenmemiş bir sabittir.

1

Go 1.13 ve sonraki sürümlerinde, pakette Value.IsZerosunulan yöntemi kullanabilirsiniz reflect.

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

Temel türlerin yanı sıra Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer ve Struct için de çalışır. Bkz bu başvuru için.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.