Je vois beaucoup de code dans Go pour détecter nil, comme ceci:
if err != nil {
// handle the error
}
cependant, j'ai une structure comme celle-ci:
type Config struct {
Host string
port float64
}
et config est une instance de Config, quand je le fais:
if config == nil {
}
il y a une erreur de compilation en disant: impossible de convertir nil en dactylographie Config
Le compilateur vous signale l'erreur, vous comparez une instance de structure avec nil. Ils ne sont pas du même type, donc il considère cela comme une comparaison invalide et vous hurle dessus.
Ce que vous voulez faire ici est de comparer un pointeur sur votre instance de configuration à nil, ce qui est une comparaison valide. Pour ce faire, vous pouvez soit utiliser la commande golang new, soit initialiser un pointeur sur celle-ci:
config := new(Config) // not nil
ou
config := &Config{
Host: "myhost.com",
port: 22,
} // not nil
ou
var config *Config // nil
Ensuite, vous pourrez vérifier si
if config == nil {
// then
}
En plus d’Oleiade, voir le spec sur les valeurs nulles :
Lorsque la mémoire est allouée pour stocker une valeur, via une déclaration ou un appel de marque ou nouvelle, et qu'aucune initialisation explicite n'est fournie, une initialisation par défaut est attribuée à la mémoire. Chaque élément d'une telle valeur est défini sur la valeur zéro pour son type: false pour les booléens, 0 pour les entiers, 0.0 pour les flottants, "" pour les chaînes et nil pour les pointeurs, fonctions et interfaces. , slices, channels et maps. Cette initialisation est effectuée de manière récursive. Ainsi, par exemple, chaque élément d’un tableau de structures aura son champ zéro si aucune valeur n’est spécifiée.
Comme vous pouvez le constater, nil
n'est pas la valeur zéro pour chaque type, mais uniquement pour les pointeurs, fonctions, interfaces, tranches, canaux et mappes. C’est la raison pour laquelle config == nil
est une erreur et &config == nil
ne l’est pas.
Pour vérifier si votre structure n'est pas initialisée, vous devez vérifier chaque membre pour sa valeur zéro respective (par exemple, Host == ""
, port == 0
, etc.) ou disposer d'un champ privé défini par une méthode d'initialisation interne. . Exemple:
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 }
J'ai créé un exemple de code qui crée de nouvelles variables en utilisant une variété de façons auxquelles je peux penser. Il semble que les trois premières manières de créer des valeurs et les deux dernières créent des références.
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)
}
qui produit:
false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}
Vous pouvez aussi vérifier comme struct_var == (struct{})
. Cela ne vous permet pas de comparer à nil mais il vérifie s'il est initialisé ou non. Soyez prudent lorsque vous utilisez cette méthode. Si votre structure peut avoir valeur zéro pour tous ses champs, vous n'aurez pas beaucoup de temps.
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
}
}
Les language spec mentionnent les comportements des opérateurs de comparaison:
Dans toute comparaison, le premier opérande doit être assignable au type du second opérande, ou inversement.
Une valeur x est assignable à une variable de type T ("x est assignable à T") dans l’un des cas suivants:
- le type de x est identique à T.
- les types de x de V et T ont des types sous-jacents identiques et au moins un de V ou T n'est pas un type nommé.
- T est un type d'interface et x implémente T.
- x est une valeur de canal bidirectionnel, T est un type de canal, le type de x V et T ont des types d'élément identiques et au moins l'un des V ou T n'est pas un type nommé.
- x est l'identifiant prédéclaré nil et T est un type de pointeur, fonction, tranche, carte, canal ou interface.
- x est une constante non typée pouvant être représentée par une valeur de type T.