обнаружение nil в Go
Я вижу много кода в Go для обнаружения nil, например:
if err != nil {
// handle the error
}
, у меня есть структура, как это:
type Config struct {
host string
port float64
}
и config-это экземпляр Config, когда я делаю:
if config == nil {
}
есть ошибка компиляции, говоря:
не удается преобразовать nil в тип Config
5 ответов:
компилятор указывает на ошибку для вас, вы сравниваете экземпляр структуры и nil. Они не одного типа, поэтому он считает это недопустимым сравнением и кричит на вас.
что вы хотите сделать здесь, это сравнить указатель на ваш экземпляр конфигурации с нулем, что является допустимым сравнением. Для этого вы можете использовать golang новая встроенные или инициализировать указатель на него:
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 способа создают значения, а последние два создают ссылки.
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{}). Это не позволяет сравнивать с нулем, но он проверяет, если он инициализирован или нет. Будьте осторожны при использовании этого метода. Если ваша структура может иметь нулевые значения для всех своих полях вы не будете иметь большое время.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 } }
The язык спецификаций упоминает поведение операторов сравнения:
в любом сравнении первый операнд должен быть присвоен типу второго операнда, или наоборот.
значение x присваивается переменной типа T ("x присваивается Т") в любом из этих случаев:
- тип x идентичен T.
- X тип V и T имеют идентичные базовые типы, и по крайней мере один из V или T не является именованным типом.
- T-Тип интерфейса, а x реализует T.
- x-значение двунаправленного канала, T-тип канала, X-тип V и T имеют одинаковые типы элементов, и по крайней мере один из V или T не является именованный тип.
- x-предварительный идентификатор nil, а T-указатель, функция, срез, карта, канал, или тип интерфейса.
- x-нетипизированная константа, представимая значением типа T.
Comments