titulos - nil detección en Go
tags h2 (5)
Veo un montón de código en Ir para detectar nada, así:
if err != nil {
// handle the error
}
sin embargo, tengo una estructura como esta:
type Config struct {
host string
port float64
}
y config es una instancia de Config, cuando lo hago:
if config == nil {
}
hay un error de compilación, diciendo: no se puede convertir nada para escribir Config
Además de Oleiade, consulte la especificación sobre valores cero :
Cuando la memoria se asigna para almacenar un valor, ya sea a través de una declaración o una llamada de make o new, y no se proporciona una inicialización explícita, la memoria recibe una inicialización predeterminada. Cada elemento de dicho valor se establece en el valor cero para su tipo: falso para booleanos, 0 para enteros, 0.0 para flotantes, "" para cadenas y nulo para punteros, funciones, interfaces, sectores, canales y mapas. Esta inicialización se realiza de forma recursiva, por lo que, por ejemplo, cada elemento de una matriz de estructuras tendrá sus campos en cero si no se especifica ningún valor.
Como puede ver, nil
no es el valor cero para cada tipo, sino solo para punteros, funciones, interfaces, sectores, canales y mapas. Esta es la razón por la cual config == nil
es un error y &config == nil
no lo es.
Para comprobar si su estructura no está inicializada, debe verificar cada miembro para su valor cero respectivo (por ejemplo, host == ""
, port == 0
, etc.) o tener un campo privado que se establece mediante un método de inicialización interno. Ejemplo:
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 }
El compilador te señala el error, estás comparando una instancia de estructura y nada. No son del mismo tipo, por lo que lo consideran una comparación no válida y te grita.
Lo que quiere hacer aquí es comparar un puntero a su instancia de configuración a nil, que es una comparación válida. Para hacerlo, puede usar el nuevo comando incorporado de golang o inicializar un puntero al mismo:
config := new(Config) // not nil
o
config := &Config{host: myhost.com, port: 22} // not nil
o
var config *Config // nil
Entonces podrás verificar si
if config == nil {
// then
}
He creado algunos ejemplos de códigos que crean nuevas variables usando una variedad de formas que puedo pensar. Parece que las primeras 3 formas crean valores y las dos últimas crean referencias.
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)
}
qué salidas:
false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}
La especificación del lenguaje menciona los comportamientos de los operadores de comparación:
En cualquier comparación, el primer operando debe ser asignable al tipo del segundo operando, o viceversa.
Un valor x se puede asignar a una variable de tipo T ("x se puede asignar a T") en cualquiera de estos casos:
- El tipo de x es idéntico a T.
- El tipo x de V y T tienen tipos subyacentes idénticos y al menos uno de V o T no es un tipo con nombre.
- T es un tipo de interfaz y x implementa T.
- x es un valor de canal bidireccional, T es un tipo de canal, x''s tipo V y T tienen tipos de elementos idénticos, y al menos uno de V o T no es un tipo con nombre.
- x es el identificador predefinido nil y T es un puntero, función, división, mapa, canal o tipo de interfaz.
- x es una constante sin tipo representable por un valor de tipo T.
También puede verificar como struct_var == (struct{})
. Esto no le permite comparar con cero, pero sí verifica si está inicializado o no. Tenga cuidado al usar este método. Si su estructura puede tener valores cero para todos sus campos, no tendrá mucho tiempo.
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
}
}