web-dev-qa-db-fra.com

Comment caster un alias de type dans Go?

Voir cet extrait de je.

Code pertinent:

type somethingFuncy func(int) bool

func funcy(i int) bool {
    return i%2 == 0
}

var a interface{} = funcy

func main() {

    _ = a.(func(int) bool)  // Works

    fmt.Println("Awesome -- apparently, literally specifying the func signature works.")

    _ = a.(somethingFuncy)  // Panics

    fmt.Println("Darn -- doesn't get here. But somethingFuncy is the same signature as func(int) bool.")
}

La première distribution fonctionne, en déclarant explicitement le type. Mais le deuxième casting panique. Pourquoi? Existe-t-il un moyen propre de diffuser une signature func plus longue?

32
Matt

tl; dr

Pour les assertions de type (que vous utilisez), seul le type réel est important. Donc somethingFuncy est seulement égal à somethingFuncy et non à func(int) bool.

Explication

Pour commencer, cela n'a rien à voir avec le casting. Il n'y a pas de casting. Il y a assertions de type et conversions de type .

Vous avez affaire à une assertion de type et supposez que les mêmes conditions s'appliquent aux conversions de type . J'ai fait la même erreur en lisant votre question, mais il y a en fait une énorme différence de comportement.

Supposons que vous ayez deux types, dites int et type MyInt int. Ceux-ci sont convertibles car ils partagent tous les deux le même type sous-jacent (l'une des règles de conversion), donc cela fonctionne ( play ):

var a int = 10
var b MyInt = MyInt(a)

Supposons maintenant que a ne soit pas de type int mais de type interface{} ( play ):

var a interface{} = int(10)
var b MyInt = MyInt(a)

Le compilateur vous dira:

ne peut pas convertir un (type interface {}) en type MyInt: besoin d'une assertion de type

Alors maintenant, nous ne faisons plus conversions mais assertions. Nous devons le faire ( play ):

var a interface{} = int(10)
var b MyInt = a.(MyInt)

Nous avons maintenant le même problème que dans votre question. Cette affirmation échoue avec cette panique:

panique: conversion d'interface: l'interface est int, pas principale.

La raison en est indiquée dans la section assertions de type de la spécification:

Pour une expression x de type interface et un type T, l'expression primaire x.(T) affirme que x n'est pas nil et que la valeur stockée dans x est de type T. La notation x.(T) est appelée une assertion de type. Plus précisément, si T n'est pas un type d'interface, x.(T) affirme que le type dynamique de x est identique au type T.

Donc int doit être identique à MyInt. Les règles de type identité indiquent que (entre autres règles):

Deux types nommés sont identiques si leurs noms de type proviennent de la même TypeSpec.

Comme int et MyInt ont des déclarations différentes ( TypeSpecs ), elles ne sont pas égales et l'assertion échoue. Lorsque vous affirmez a à int, l'assertion fonctionne. Donc ce que vous faites n'est pas possible.

Prime:

La vérification réelle se produit dans ce code , qui vérifie simplement si les deux types sont identiques, comme prévu.

56
nemo

Mise à jour 2017:

Avec les assertions de type dans Go 1.9, vous pouvez simplement ajouter = Où vous définissez le type.

type somethingFuncy = func(int) bool

Cela indique au compilateur que somethingFuncy est un autre nom pour func(int) bool.

16
agarfield

Juste pour compléter la réponse impressionnante de Nemo, notez que même si vous ne pouvez pas sauter directement à partir d'une interface (par exemple, interface{}) d'un type dynamique donné (par exemple, int) vers un autre type (par exemple, type MyInt int), vous pouvez effectuer les deux étapes l'une après l'autre:

  • affirmer que le type dynamique de votre variable est ce que vous attendez d'elle;
  • convertir le résultat de cette assertion dans le type de votre choix.

Notez que puisque le type sous-jacent est, comme son nom l'indique, dynamique , c'est une bonne idée de tester si l'assertion de type a réussi ou échoué. D'autre part, l'exactitude de la conversion de type est appliquée par le compilateur.

Voici votre extrait de terrain de jeu légèrement modifié: http://play.golang.org/p/FZv06Zf7xi

7
FelixCQ

Je crois que cet alias de type est ce que vous voulez. La proposition est acceptée et devrait être dans Go 1.9. C'est à dire.

TypeSpec = identifier [ "=" ] Type .

Les références
https://github.com/golang/go/issues/181
https://github.com/golang/proposal/blob/master/design/18130-type-alias.md

1
Anssi