web-dev-qa-db-fra.com

Convertir une interface nulle en pointeur de quelque chose à Golang?

Dans le code suivant, la tentative de conversion d'une interface nil en un pointeur de quelque chose échoue avec l'erreur suivante: interface conversion: interface is nil, not *main.Node

type Nexter interface {
    Next() Nexter
}

type Node struct {
    next Nexter
}

func (n *Node) Next() Nexter {...}

func main() {
    var p Nexter

    var n *Node
    fmt.Println(n == nil) // will print true
    n = p.(*Node) // will fail
}

Lien de lecture ici: https://play.golang.org/p/2cgyfUStCI

Pourquoi cela échoue-t-il exactement? Il est tout à fait possible de faire

n = (*Node)(nil)

, je me demande donc comment obtenir un effet similaire à partir d'une interface nulle.

14
Mihnea Giurgea

En effet, une variable de type statique type Nexter (qui n'est qu'une interface) peut contenir des valeurs de nombreux types dynamique différents.

Oui, puisque *Node Implémente Nexter, votre p variable may contient une valeur de type *Node, Mais elle peut hold autres types ainsi qui implémentent Nexter; ou il peut contenir rien du tout (nil value). Et Assertion de type ne peut pas être utilisé ici car citation de la spécification:

x.(T) affirme que x n'est pas pas nil et que la valeur stockée dans x est de type T.

Mais x dans votre cas est nil. Et si l'assertion de type est fausse, une panique au moment de l'exécution se produit .

Si vous modifiez votre programme pour initialiser votre variable p avec:

var p Nexter = (*Node)(nil)

Votre programme s'exécutera et l'assertion de type réussira. En effet, une valeur d'interface contient en fait une paire sous la forme de: (value, dynamic type), Et dans ce cas, votre p ne sera pas nil, mais contiendra une paire de (nil, *Node); pour plus de détails, voir Les lois de la réflexion #La représentation d'une interface .

Si vous souhaitez également gérer les valeurs nil des types d'interface, vous pouvez le vérifier explicitement comme ceci:

if p != nil {
    n = p.(*Node) // will not fail IF p really contains a value of type *Node
}

Ou mieux: utilisez le formulaire spécial "virgule-ok":

// This will never fail:
if n, ok := p.(*Node); ok {
    fmt.Printf("n=%#v\n", n)
}

Utilisation du formulaire "virgule-ok":

La valeur de ok est true si l'assertion est vraie. Sinon, c'est false et la valeur de n est la valeur zéro pour le type T. Aucune panique d'exécution ne se produit dans ce cas.

31
icza