2013-11-27 4 views
93

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

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

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

type Config struct { 
    host string 
    port float64 
} 

и конфигурация является экземпляром из Config, когда я делаю:

if config == nil { 
} 

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

ответ

117

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

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

config := new(Config) // not nil 

или

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

или

var config *Config // nil 

Тогда вы будете в состоянии для проверки, если

if config == nil { 
    // then 
} 
+5

Я предполагаю 'вар конфигурации и конфигурации // nil' должен быть:' вар конфигурации * Config' –

+0

'вар конфигурации * Config' сбой с недопустимым адресом памяти или разыменованием нулевого указателя. Может быть, нам нужно «var config Config» – kachar

+0

Я понимаю, что аргументация позади этого выбора может быть не вашей, но она меняет * no * смысл, что «if! (Config! = Nil)» действительно, но «если config == ноль "нет. Оба делают сравнение между одной и той же структурой и неструктурой. – retorquere

47

В дополнении к Oleiade, см spec on zero values:

Когда память выделяется для хранения значения, либо с помощью декларации или вызова марки или нового, и без явной инициализации предусмотрена память даются инициализация по умолчанию. Каждый элемент такого значения устанавливается равным нулю для его типа: false для booleans, 0 для целых чисел, 0.0 для float, «" для строк и 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; если t1 == time.Time {} '] (https://play.golang.org/p/n7EfFVlzrB), и вы * можете * также выполнить' if config == Config {} ', чтобы проверить все поле для вас (структурное равенство хорошо определено в Go). Однако это не эффективно, если у вас много полей. И, возможно, нулевое значение - разумное и полезное значение, поэтому передача одного из них не является особенной. –

+1

Инициализированная функция завершится с ошибкой, если доступ к Config в качестве указателя. Его можно было бы изменить на 'func (c * Config) Initialized() bool {return!(c == nil)} ' – Sundar

+0

@ Сундар в этом случае было бы удобно сделать это таким образом, поэтому я применил это изменение. Однако, как правило, я бы не ожидал, что принимающая сторона вызова метода проверит, является ли она нулевой, поскольку это должно быть заданием вызывающего. – nemo

12

Я создал некоторый пример кода, который создает новую переменную, используя различные способы, о которых я могу думать. Похоже, что первые 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} 
5

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

3

language spec упоминает поведение операторов сравнения:

comparison operators

В любом сравнении, первый операнд должен быть отнесен к типу второго операнда, или наоборот.


Assignability

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

  • х годов тип идентичен T.
  • Тип x типа V и T имеют одинаковые базовые типы, и по крайней мере один из V или T не является именем d тип.
  • Т является тип интерфейса и х реализует Т.
  • х является двунаправленным значение канала, Т представляет собой тип канала, X Тип V и Т имеют одинаковые типы элементов, и по меньшей мере один из V или Т не именованный тип.
  • x - это предопределенный идентификатор nil, а T - указатель, функция, срез, карта, канал или тип интерфейса.
  • х представляет собой константу нетипизированным представима значение типа T.
Смежные вопросы