Goには、次のようにnilを検出するための多くのコードがあります。
if err != nil {
// handle the error
}
ただし、次のような構造体があります。
type Config struct {
Host string
port float64
}
configはConfigのインスタンスです。
if config == nil {
}
コンパイルエラーがあります:nilをタイプConfigに変換できません
コンパイラはエラーを指摘しています。構造インスタンスと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
}
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 }
私が考えることができるさまざまな方法を使用して新しい変数を作成するサンプルコードをいくつか作成しました。最初の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}
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
}
}
言語仕様 は比較演算子の動作に言及しています:
すべての比較において、最初のオペランドは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の値で表現できる型なし定数です。