Comment faire un littéral * int64 dans Go?

103

J'ai un type struct avec un *int64champ.

type SomeType struct {
    SomeField *int64
}

À un moment donné dans mon code, je veux déclarer un littéral de ceci (disons, quand je sais que ladite valeur devrait être 0, ou pointer vers un 0, vous savez ce que je veux dire)

instance := SomeType{
    SomeField: &0,
}

... sauf que cela ne fonctionne pas

./main.go:xx: cannot use &0 (type *int) as type *int64 in field value

Alors j'essaye ça

instance := SomeType{
    SomeField: &int64(0),
}

... mais cela ne fonctionne pas non plus

./main.go:xx: cannot take the address of int64(0)

Comment puis-je faire cela? La seule solution que je peux trouver est d'utiliser une variable d'espace réservé

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Remarque: la &0syntaxe fonctionne bien quand c'est un * int au lieu d'un *int64. Edit: non, ce n'est pas le cas. Désolé pour ça.

Éditer:

Apparemment, ma question était trop ambiguë. Je cherche un moyen de déclarer littéralement a *int64. Cela pourrait être utilisé dans un constructeur, ou pour énoncer des valeurs de struct littérales, ou même comme arguments pour d'autres fonctions. Mais les fonctions d'assistance ou l'utilisation d'un type différent ne sont pas des solutions que je recherche.

ThisGuy
la source
1
Les pointeurs vers int sont malheureux car le int prend la même quantité d'espace qu'un pointeur, vous n'économisez donc pas de place. Il ajoute simplement une valeur NULL qui est généralement juste plus complexe qu'elle ne vaut. Dans la plupart des cas, un 0 conviendrait. Si vous avez besoin d'une valeur supplémentaire, un booléen "IsValidSomeField" fonctionne également et si vous donnez à ce booléen un meilleur nom, cela peut en dire plus sur la raison pour laquelle vous avez besoin de cette valeur supplémentaire, ce qui est bon pour la lisibilité.
voutasaurus
2
Vous pouvez utiliser le pointeur de package , par exemple:var _ *int64 = pointer.Int64(64)
Xor

Réponses:

212

La spécification du langage Go ( opérateurs d'adresse ) ne permet pas de prendre l'adresse d'une constante numérique (pas d'une constante non typée ni d'une constante typée ).

L'opérande doit être adressable , c'est-à-dire une opération d'indexation de variable, d'indirection de pointeur ou de tranche; ou un sélecteur de champ d'un opérande struct adressable; ou une opération d'indexation de tableau d'un tableau adressable. En tant qu'exception à l'exigence d'adressabilité, x[dans l'expression de &x] peut également être un littéral composite (éventuellement entre parenthèses) .

Pour savoir pourquoi cela n'est pas autorisé, consultez la question associée: Rechercher l'adresse de la constante dans go . Une question similaire (de même pas autorisé à prendre son adresse): Comment puis-je stocker une référence au résultat d'une opération dans Go?

Vos options (tout essayer sur le Go Playground ):

1) Avec new()

Vous pouvez simplement utiliser la new()fonction intégrée pour allouer une nouvelle valeur zéro int64et obtenir son adresse:

instance := SomeType{
    SomeField: new(int64),
}

Mais notez que cela ne peut être utilisé que pour allouer et obtenir un pointeur vers la valeur zéro de tout type.

2) Avec variable d'assistance

Le plus simple et recommandé pour les éléments non nuls est d'utiliser une variable d'assistance dont l'adresse peut être prise:

helper := int64(2)
instance2 := SomeType{
    SomeField: &helper,
}

3) avec fonction d'assistance

Remarque: les fonctions d'assistance pour acquérir un pointeur vers une valeur non nulle sont disponibles dans ma github.com/icza/goxbibliothèque, dans le goxpackage, vous n'avez donc pas à les ajouter à tous vos projets là où vous en avez besoin.

Ou si vous en avez besoin plusieurs fois, vous pouvez créer une fonction d'assistance qui alloue et renvoie un *int64:

func create(x int64) *int64 {
    return &x
}

Et en l'utilisant:

instance3 := SomeType{
    SomeField: create(3),
}

Notez qu'en fait nous n'avons rien alloué, le compilateur Go l'a fait lorsque nous avons renvoyé l'adresse de l'argument de la fonction. Le compilateur Go effectue une analyse d'échappement et alloue des variables locales sur le tas (au lieu de la pile) si elles peuvent échapper à la fonction. Pour plus de détails, voir Le retour d'une tranche d'un tableau local dans une fonction Go est-il sûr?

4) Avec une fonction anonyme sur une seule ligne

instance4 := SomeType{
    SomeField: func() *int64 { i := int64(4); return &i }(),
}

Ou comme alternative (plus courte):

instance4 := SomeType{
    SomeField: func(i int64) *int64 { return &i }(4),
}

5) Avec slice littéral, indexation et prise d'adresse

Si vous voulez *SomeFieldêtre autre que 0, alors vous avez besoin de quelque chose d'adressable.

Vous pouvez toujours faire ça, mais c'est moche:

instance5 := SomeType{
    SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5

Ce qui se passe ici, c'est qu'une []int64tranche est créée avec un littéral, ayant un élément ( 5). Et il est indexé (0e élément) et l'adresse du 0e élément est prise. En arrière-plan, un tableau de [1]int64sera également alloué et utilisé comme tableau de sauvegarde pour la tranche. Il y a donc beaucoup de passe-partout ici.

6) Avec une structure d'aide littérale

Examinons l'exception aux exigences d'adressabilité:

En tant qu'exception à l'exigence d'adressabilité, x[dans l'expression de &x] peut également être un littéral composite (éventuellement entre parenthèses) .

Cela signifie que prendre l'adresse d'un littéral composite, par exemple un littéral struct est ok. Si nous le faisons, nous aurons la valeur struct allouée et un pointeur obtenu vers elle. Mais si c'est le cas, une autre exigence deviendra disponible pour nous: "sélecteur de champ d'un opérande struct adressable" . Donc, si le littéral struct contient un champ de type int64, on peut également prendre l'adresse de ce champ!

Voyons cette option en action. Nous utiliserons ce type de structure wrapper:

type intwrapper struct {
    x int64
}

Et maintenant nous pouvons faire:

instance6 := SomeType{
    SomeField: &(&intwrapper{6}).x,
}

Notez que ceci

&(&intwrapper{6}).x

signifie ce qui suit:

& ( (&intwrapper{6}).x )

Mais nous pouvons omettre la parenthèse "externe" car l'opérateur d'adresse &est appliqué au résultat de l' expression de sélecteur .

Notez également qu'en arrière-plan, ce qui suit se produira (c'est également une syntaxe valide):

&(*(&intwrapper{6})).x

7) Avec helper anonyme struct literal

Le principe est le même que dans le cas n ° 6, mais nous pouvons également utiliser un littéral struct anonyme, donc aucune définition de type struct helper / wrapper n'est nécessaire:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
la source
1
ceci est fonctionnellement différent du dernier exemple de la question avec l'espace réservé, puisque deux instanceobjets différents pointeraient vers deux int64s différents . Mais cela semble répondre correctement à l'intention des PO
Ryan Haining
2
cela répond définitivement à ma question. Mais cela me rend assez bouleversé: &[]int64{2}[0]je pense que, d'après la description des constantes sur blog.golang.org/constants, cela devrait fonctionner comme &0.
ThisGuy
@RyanHaining Et que se passerait-il si cela fonctionnait comme si la même adresse était attribuée? Si deux instanceobjets différents int64pointaient vers le même et que l'un modifiait l'objet pointé, les deux changeraient. Et si maintenant vous utilisiez le même littéral pour créer un 3e instance? La même adresse pointerait maintenant vers une int64valeur différente ... donc une adresse différente devrait être utilisée, mais alors pourquoi utiliser la même dans le cas des 2 premiers?
icza
@icza, vous ne voudriez généralement pas qu'ils pointent vers le même objet, je ne dis pas que vous le feriez. Je fais juste remarquer la différence.
Ryan Haining
4
Les constantes @Conslo sont évaluées au moment de la compilation. Une valeur de pointeur valide, une adresse mémoire valide n'existe qu'au moment de l'exécution, donc ce n'est pas la même chose que les constantes.
icza
6

Utilisez une fonction qui renvoie une adresse d'une variable int64 pour résoudre le problème.

Dans le code ci-dessous, nous utilisons une fonction fqui accepte un entier et renvoie une valeur de pointeur qui contient l'adresse de l'entier. En utilisant cette méthode, nous pouvons facilement résoudre le problème ci-dessus.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
ASHWIN RAJEEV
la source