Goでのnil検出


165

Goには、次のようにnilを検出するためのコードがたくさんあります。

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

しかし、私はこのような構造体を持っています:

type Config struct {
    host string  
    port float64
}

そして私がするとき、configはConfigのインスタンスです:

if config == nil {
}

コンパイルエラーがあり、nilをConfig型に変換できません


3
ポートがfloat64型である理由がわかりません。
アラミン

2
それはいけません。GoのJSON APIは、JSONからfloat64に任意の数をインポートします。float64をintに変換する必要があります。
Qian Chen、

回答:


179

コンパイラがエラーを指摘しています。構造体インスタンスとnilを比較しています。それらは同じタイプではないため、無効な比較と見なされ、怒鳴られます。

ここで行うことは、構成インスタンスへのポインターをnilと比較することです。これは有効な比較です。これを行うには、golang newビルトインを使用するか、それへのポインターを初期化します。

config := new(Config) // not nil

または

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

または

var config *Config // nil

次に、あなたはかどうかを確認することができます

if config == nil {
    // then
}

5
私の推測では、var config &Config // nil次のようになりますvar config *Config
トマシュPlonka

var config *Configでクラッシュしinvalid memory address or nil pointer dereferenceます。多分私たちが必要var config Config
kachar

この選択の背後にある理由があなたのものではない可能性があることは理解していますが、「if!(config!= nil)」は有効であるが「if config == nil」は無効であることは私に意味がありません。両方が同じ構造体と非構造体を比較しています。
1

@retorquere彼らは両方とも無効である、参照play.golang.org/p/k2EmRcels6を。「!=」でも「==」でも違いはありません。違いは、configが構造体であるか、構造体へのポインタであるかです。
stewbasic 2017年

私はそれが常に偽であるとして、これは、間違っていると思う:play.golang.org/p/g-MdbEbnyNx
Madeo

61

オレイアデに加えて、ゼロ値に関する仕様を参照してください:

宣言またはmakeまたはnewの呼び出しによって値を格納するためにメモリが割り当てられ、明示的な初期化が提供されない場合、メモリにはデフォルトの初期化が与えられます。そのような値の各要素は、そのタイプのゼロ値に設定されます。ブール値の場合はfalse、整数の場合は0、浮動小数点数の場合は0.0、文字列の場合は ""、ポインター、関数、インターフェイス、スライス、チャネル、およびマップの場合はnilです。この初期化は再帰的に行われるため、たとえば、値が指定されていない場合、構造体の配列の各要素のフィールドはゼロになります。

ご覧のとおり、nilはすべてのタイプのゼロ値ではなく、ポインター、関数、インターフェース、スライス、チャネル、およびマップに対してのみです。これがconfig == nilエラーであり、エラーで&config == nilはない理由です 。

あなたの構造体が初期化されていないかどうかを確認するには、(例えば、それぞれのゼロ値のためのすべてのメンバーをチェックする必要があるだろうhost == ""port == 0など)や内部の初期化メソッドによって設定されたプライベートなフィールドを持っています。例:

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
上記に加えて、それがメソッドをtime.Time持つ理由IsZero()です。ただし、すべてのフィールドをチェックvar t1 time.Time; if t1 == time.Time{}することできif config == Config{}ます(構造の等価性はGoで明確に定義されています)。ただし、フィールドが多い場合は効率的ではありません。そして、おそらくゼロ値は正気で使いやすい値なので、1を渡すことは特別ではありません。
Dave C、

1
ポインタとしてのConfigにアクセスすると、Initialized関数は失敗します。次のように変更できますfunc (c *Config) Initialized() bool { return !(c == nil) }
サンダー

この場合の@Sundarは、このようにすると便利な場合があるため、変更を適用しました。ただし、通常はメソッド呼び出しの受信側がそれ自体がnilかどうかを確認することは期待していません。これは呼び出し側の仕事であるためです。
nemo

16

私はさまざまな方法で新しい変数を作成するサンプルコードを作成しました。最初の3つの方法で値を作成し、最後の2つの方法で参照を作成するように見えます。

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

出力:

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

6

のように確認することもできstruct_var == (struct{})ます。これはnilと比較することはできませんが、初期化されているかどうかをチェックします。この方法を使用するときは注意してください。構造体のすべてのフィールドのゼロになる可能性がある場合、時間はあまりありません。

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

言語仕様は、比較演算子の行動に言及しています:

比較演算子

どの比較でも、最初のオペランドは2番目のオペランドのタイプに割り当て可能である必要があり、その逆も可能です。


割り当て可能性

次のいずれかの場合、値xはタイプTの変数に割り当て可能です(「xはTに割り当て可能」)。

  • xの型はTと同じです。
  • xのタイプVとTの基礎となるタイプが同一であり、VまたはTの少なくとも1つが名前付きタイプではありません。
  • Tはインターフェース型であり、xはTを実装します。
  • xは双方向チャネル値、Tはチャネルタイプ、xのタイプVとTは同じ要素タイプを持ち、VまたはTの少なくとも1つは名前付きタイプではありません。
  • xは事前宣言された識別子nilであり、Tはポインター、関数、スライス、マップ、チャネル、またはインターフェースタイプです。
  • xは、型Tの値で表現できる型なし定数です。

0

Go 1.13以降でValue.IsZeroは、reflectパッケージで提供されるメソッドを使用できます。

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

基本的なタイプとは別に、Array、Chan、Func、Interface、Map、Ptr、Slice、UnsafePointer、およびStructでも機能します。参照してください。これを参考のために。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.