Wie mache ich ein Literal * int64 in Go?

100

Ich habe einen Strukturtyp mit einem *int64Feld.

type SomeType struct {
    SomeField *int64
}

Irgendwann in meinem Code möchte ich ein Literal davon deklarieren (sagen wir, wenn ich weiß, dass der Wert 0 sein sollte, oder wenn ich auf eine 0 zeige, wissen Sie, was ich meine)

instance := SomeType{
    SomeField: &0,
}

... außer das funktioniert nicht

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

Also versuche ich das

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

... aber das funktioniert auch nicht

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

Wie mache ich das? Die einzige Lösung, die ich finden kann, ist die Verwendung einer Platzhaltervariablen

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Hinweis: Die &0Syntax funktioniert einwandfrei, wenn es sich um ein * int anstelle eines handelt *int64. Edit: nein tut es nicht. Tut mir leid.

Bearbeiten:

Anscheinend war meine Frage zu vieldeutig. Ich bin auf der Suche nach einem Weg , um buchstäblich Zustand ein *int64. Dies kann innerhalb eines Konstruktors oder zur Angabe von Literalstrukturwerten oder sogar als Argumente für andere Funktionen verwendet werden. Aber Hilfsfunktionen oder die Verwendung eines anderen Typs sind keine Lösungen, nach denen ich suche.

Dieser Typ
quelle
1
Zeiger auf int sind unglücklich, da int genauso viel Platz wie ein Zeiger einnimmt, sodass Sie keinen Platz sparen. Es wird nur ein NULL-Wert hinzugefügt, der normalerweise nur komplexer ist als es wert ist. In den meisten Fällen wäre eine 0 in Ordnung. Wenn Sie einen zusätzlichen Wert benötigen, funktioniert auch ein "IsValidSomeField" -Bool. Wenn Sie diesem Bool einen besseren Namen geben, kann er mehr darüber aussagen, warum Sie diesen zusätzlichen Wert benötigen, was für die Lesbarkeit gut ist.
Voutasaurus
1
Sie können Paket verwenden Zeiger , zum Beispiel:var _ *int64 = pointer.Int64(64)
Xor

Antworten:

210

Die Go Language Specification ( Adreßoperatoren ) erlaubt nicht die Adresse einer numerischen Konstante zu nehmen (nicht von einem nicht typisiert noch eine typisierten Konstante).

Der Operand muss adressierbar sein , dh entweder eine Variable, eine Zeiger-Indirektion oder eine Slice-Indizierungsoperation. oder ein Feldselektor eines adressierbaren Strukturoperanden; oder eine Array-Indizierungsoperation eines adressierbaren Arrays. Als Ausnahme von der Adressierbarkeitsanforderung kann x[im Ausdruck von &x] auch ein (möglicherweise in Klammern gesetztes) zusammengesetztes Literal sein .

Gründe, warum dies nicht zulässig ist, finden Sie in der entsprechenden Frage: Suchen Sie die Adresse der Konstanten in go . Eine ähnliche Frage (die ebenfalls keine Adresse annehmen darf): Wie kann ich einen Verweis auf das Ergebnis einer Operation in Go speichern?

Ihre Optionen (probieren Sie alle auf dem Go-Spielplatz aus ):

1) Mit new()

Sie können einfach die eingebaute new()Funktion verwenden, um einen neuen Nullwert zuzuweisen int64und seine Adresse zu erhalten:

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

Beachten Sie jedoch, dass dies nur verwendet werden kann, um einen Zeiger auf den Nullwert eines beliebigen Typs zuzuweisen und zu erhalten.

2) Mit Hilfsvariable

Am einfachsten und empfehlenswert für Elemente ungleich Null ist die Verwendung einer Hilfsvariablen, deren Adresse verwendet werden kann:

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

3) Mit Hilfsfunktion

Hinweis: Hilfsfunktionen zum Erfassen eines Zeigers auf einen Wert ungleich Null sind in meiner github.com/icza/goxBibliothek im goxPaket verfügbar , sodass Sie diese nicht zu allen Ihren Projekten hinzufügen müssen, wo Sie sie benötigen.

Wenn Sie dies mehrmals benötigen, können Sie eine Hilfsfunktion erstellen, die Folgendes zuweist und zurückgibt *int64:

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

Und damit:

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

Beachten Sie, dass wir tatsächlich nichts zugewiesen haben. Der Go-Compiler hat dies getan, als wir die Adresse des Funktionsarguments zurückgegeben haben. Der Go-Compiler führt eine Escape-Analyse durch und weist dem Heap (anstelle des Stacks) lokale Variablen zu, wenn sie der Funktion entkommen können. Weitere Informationen finden Sie unter Ist die Rückgabe eines Slice eines lokalen Arrays in einer Go-Funktion sicher?

4) Mit einer einzeiligen anonymen Funktion

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

Oder als (kürzere) Alternative:

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

5) Mit Slice-Literal, Indizierung und Adressierung

Wenn Sie *SomeFieldanders sein möchten als 0, dann brauchen Sie etwas Adressierbares.

Sie können das immer noch tun, aber das ist hässlich:

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

Was hier passiert, ist, dass ein []int64Slice mit einem Literal erstellt wird, das ein Element ( 5) enthält. Und es wird indiziert (0. Element) und die Adresse des 0. Elements wird genommen. Im Hintergrund [1]int64wird auch ein Array von zugewiesen und als Hintergrundarray für das Slice verwendet. Hier gibt es also viel Boilerplate.

6) Mit einem Hilfsstrukturliteral

Lassen Sie uns die Ausnahme von den Adressierbarkeitsanforderungen untersuchen:

Als Ausnahme von der Adressierbarkeitsanforderung kann x[im Ausdruck von &x] auch ein (möglicherweise in Klammern gesetztes) zusammengesetztes Literal sein .

Dies bedeutet, dass es in Ordnung ist, die Adresse eines zusammengesetzten Literal zu übernehmen, z. B. eines Strukturliterals. Wenn wir dies tun, wird uns der Strukturwert zugewiesen und ein Zeiger darauf erhalten. In diesem Fall steht uns jedoch eine weitere Anforderung zur Verfügung: "Feldauswahl eines adressierbaren Strukturoperanden" . Wenn das Strukturliteral also ein Feld vom Typ enthält int64, können wir auch die Adresse dieses Feldes übernehmen!

Lassen Sie uns diese Option in Aktion sehen. Wir werden diesen Wrapper-Strukturtyp verwenden:

type intwrapper struct {
    x int64
}

Und jetzt können wir tun:

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

Beachten Sie, dass dies

&(&intwrapper{6}).x

bedeutet folgendes:

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

Wir können jedoch die "äußere" Klammer weglassen, da der Adressoperator &auf das Ergebnis des Selektorausdrucks angewendet wird .

Beachten Sie auch, dass im Hintergrund Folgendes passiert (dies ist auch eine gültige Syntax):

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

7) Mit dem anonymen Strukturliteral des Helfers

Das Prinzip ist das gleiche wie in Fall 6, wir können jedoch auch ein anonymes Strukturliteral verwenden, sodass keine Definition des Strukturtyps für Helfer / Wrapper erforderlich ist:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
quelle
1
Dies unterscheidet sich funktional vom letzten Beispiel in der Frage mit dem Platzhalter, da zwei unterschiedliche instanceObjekte auf zwei verschiedene int64s verweisen würden . Aber es scheint die Absicht der OP angemessen zu erfüllen
Ryan Haining
2
Dies beantwortet definitiv meine Frage. Aber es macht mich ganz aufgeregt: &[]int64{2}[0], fühle ich mich wie auf der Grundlage der Beschreibung der Konstanten bei blog.golang.org/constants dies sollte nur Arbeit als &0.
ThisGuy
@ RyanHaining Und was würde passieren, wenn es so funktionieren würde, als würde dieselbe Adresse zugewiesen? Wenn zwei verschiedene instanceObjekte auf dasselbe zeigen int64und eines das spitze Objekt modifizieren würde, würden sich beide ändern. Und was ist, wenn Sie jetzt dasselbe Literal verwenden, um ein drittes zu erstellen instance? Dieselbe Adresse würde jetzt auf einen anderen int64Wert verweisen ... daher sollte eine andere Adresse verwendet werden, aber warum sollte im Fall der ersten 2 dieselbe Adresse verwendet werden?
icza
@icza Normalerweise möchten Sie nicht, dass sie auf dasselbe Objekt zeigen. Ich sage nicht, dass Sie dies tun würden. Ich weise nur auf den Unterschied hin.
Ryan Haining
4
@ Conslo-Konstanten werden zur Kompilierungszeit ausgewertet. Ein gültiger Zeigerwert, eine gültige Speicheradresse ist nur zur Laufzeit vorhanden, daher ist dies nicht dasselbe wie Konstanten.
icza
6

Verwenden Sie eine Funktion, die eine Adresse einer int64-Variablen zurückgibt, um das Problem zu lösen.

Im folgenden Code verwenden wir eine Funktion, fdie eine Ganzzahl akzeptiert und einen Zeigerwert zurückgibt, der die Adresse der Ganzzahl enthält. Mit dieser Methode können wir das obige Problem leicht lösen.

type myStr struct {
    url *int64
}

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