web-dev-qa-db-ja.com

Goでのnil検出

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

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

ただし、次のような構造体があります。

type Config struct {
    Host string  
    port float64
}

configはConfigのインスタンスです。

if config == nil {
}

コンパイルエラーがあります:nilをタイプConfigに変換できません

129
Qian Chen

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

ここでやりたいことは、設定インスタンスへのポインタをnilと比較することです。これは有効な比較です。そのためには、golang new builtinを使用するか、それへのポインターを初期化します。

config := new(Config) // not nil

または

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

または

var config *Config // nil

その後、次のことを確認できます。

if config == nil {
    // then
}
157
Oleiade

Oleiadeに加えて、 ゼロ値の仕様 を参照してください。

宣言または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 }
55
nemo

私が考えることができるさまざまな方法を使用して新しい変数を作成するサンプルコードをいくつか作成しました。最初の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}
16
Qian Chen

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

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

6
Thellimist

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

比較演算子

すべての比較において、最初のオペランドは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の値で表現できる型なし定数です。
3
supei