Nil в Go

Я вижу много кода в Go для обнаружения nil, например:

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

однако у меня есть такая структура:

type Config struct {
    host string  
    port float64
}

и config - это экземпляр Config, когда я это делаю:

if config == nil {
}

есть ошибка компиляции, говоря:   не может преобразовать nil в тип Config

Ответ 1

Компилятор указывает на ошибку для вас, вы сравниваете экземпляр структуры и ноль. Они не одного типа, поэтому он считает это недопустимым сравнением и кричит на вас.

Что вы хотите сделать, это сравнить указатель на ваш экземпляр config на nil, что является допустимым сравнением. Для этого вы можете использовать новый встроенный golang или инициализировать указатель на него:

config := new(Config) // not nil

или

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

или

var config *Config // nil

Затем вы сможете проверить,

if config == nil {
    // then
}

Ответ 2

В дополнение к Oleiade, см. spec для нулевых значений:

Когда память выделяется для хранения значения, либо через объявление, либо вызов make, либо новый, и явная инициализация не предоставляется, памяти присваивается инициализация по умолчанию. Каждый элемент такого значения устанавливается равным нулю для своего типа: false для booleans, 0 для целых чисел, 0.0 для float, "" для строк и nil для указателей, функций, интерфейсов, срезов, каналов и maps. Эта инициализация выполняется рекурсивно, поэтому, например, каждый элемент массива структур будет иметь свои поля, если они не заданы.

Как вы можете видеть, 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

Я создал некоторый пример кода, который создает новую переменную, используя различные способы, о которых я могу думать. Похоже, что первые 3 способа создают значения, а последние два создают ссылки.

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}

Ответ 4

Вы также можете проверить, как 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

Ответ 5

спецификация языка описывает поведение операторов сравнения:

операторы сравнения

В любом сравнении первый операнд должен быть назначен для типа второго операнда или наоборот.


Assignability

Значение x присваивается переменной типа T ( "x присваивается T" ) в любом из этих случаев:

  • x type идентичен T.
  • x тип V и T имеют одинаковые базовые типы, и по крайней мере один из V или T не является именованным типом.
  • T - тип интерфейса, а x реализует T.
  • x - двунаправленное значение канала, T - тип канала, x тип V и T имеют одинаковые типы элементов, и по крайней мере один из V или T не является именованный тип.
  • x - это предопределенный идентификатор nil, а T - тип указателя, функции, среза, карты, канала или интерфейса.
  • x - нетипизированная константа, представляемая значением типа T.