Jak zrobić dosłowne * int64 w Go?

113

Mam typ struktury z *int64polem.

type SomeType struct {
    SomeField *int64
}

W pewnym momencie w moim kodzie chcę zadeklarować literał tego (powiedzmy, kiedy wiem, że wspomniana wartość powinna wynosić 0 lub wskazująca na 0, wiesz, o co mi chodzi)

instance := SomeType{
    SomeField: &0,
}

... poza tym, że to nie działa

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

Więc próbuję tego

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

... ale to też nie działa

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

Jak mam to zrobic? Jedynym rozwiązaniem, które mogę wymyślić, jest użycie zmiennej zastępczej

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Uwaga: &0składnia działa dobrze, gdy jest to * int zamiast *int64. Edycja: nie, nie. Przepraszam za to.

Edytować:

Najwyraźniej moje pytanie było zbyt wieloznaczne. Szukam sposobu na dosłownie państwowej A *int64. Można tego użyć wewnątrz konstruktora lub do określenia wartości struktury literału, a nawet jako argumenty innych funkcji. Ale funkcje pomocnicze lub używanie innego typu nie są rozwiązaniami, których szukam.

Ten facet
źródło
1
Wskaźniki na int są niefortunne, ponieważ int zajmuje tyle samo miejsca co wskaźnik, więc nie oszczędzasz miejsca. Po prostu dodaje wartość NULL, która zwykle jest po prostu bardziej złożona niż jest warta. W większości przypadków 0 wystarczy. Jeśli potrzebujesz dodatkowej wartości, zadziała również wartość „IsValidSomeField”, a jeśli nadasz temu boolowi lepszą nazwę, może powiedzieć więcej o tym, dlaczego potrzebujesz tej dodatkowej wartości, która jest dobra dla czytelności.
voutasaurus
3
Możesz użyć wskaźnika pakietu , na przykład:var _ *int64 = pointer.Int64(64)
Xor
Szkoda, że ​​musimy napisać funkcję, a nawet bibliotekę, aby móc to zrobić.
Coconut

Odpowiedzi:

226

Specyfikacja języka Go ( operatory adresu ) nie pozwala na przyjęcie adresu stałej numerycznej (nie bez typu ani stałej wpisanej ).

Operand musi być adresowalny , to znaczy zmienna, wskaźnik pośredni lub operacja indeksowania wycinka; lub selektor pola adresowalnego argumentu struktury; lub operacja indeksowania tablicy adresowalnej. Jako wyjątek od wymagania adresowalności, x[w wyrażeniu &x] może być również (prawdopodobnie umieszczonym w nawiasach) literałem złożonym .

Aby wyjaśnić, dlaczego jest to niedozwolone, zobacz powiązane pytanie: Znajdź adres stałej w ruchu . Podobne pytanie (podobnie nie można wziąć jego adresu): Jak mogę zapisać odniesienie do wyniku operacji w Go?

Twoje opcje (wypróbuj wszystkie na Go Playground ):

1) Z new()

Możesz po prostu użyć new()funkcji wbudowanej, aby przydzielić nową wartość zerową int64i uzyskać jej adres:

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

Należy jednak pamiętać, że można tego użyć tylko do przydzielenia i uzyskania wskaźnika do wartości zerowej dowolnego typu.

2) Ze zmienną pomocniczą

Najprostszym i zalecanym dla niezerowych elementów jest użycie zmiennej pomocniczej, której adres można pobrać:

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

3) Z funkcją pomocniczą

Uwaga: Funkcje pomocnicze do uzyskiwania wskaźnika do wartości niezerowej są dostępne w mojej github.com/icza/goxbibliotece, w goxpakiecie, więc nie musisz ich dodawać do wszystkich projektów, w których jest to potrzebne.

Lub jeśli potrzebujesz tego wiele razy, możesz utworzyć funkcję pomocniczą, która przydziela i zwraca *int64:

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

I używając go:

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

Zauważ, że w rzeczywistości niczego nie alokowaliśmy, kompilator Go zrobił to, kiedy zwróciliśmy adres argumentu funkcji. Kompilator Go wykonuje analizę ucieczki i przydziela zmienne lokalne na stercie (zamiast na stosie), jeśli mogą one uciec przed funkcją. Aby uzyskać szczegółowe informacje, zobacz Czy zwracanie wycinka tablicy lokalnej w funkcji Go jest bezpieczne?

4) Z jedną linijką anonimową funkcją

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

Lub jako (krótsza) alternatywa:

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

5) Z dosłownym wycinkiem, indeksowaniem i przyjmowaniem adresu

Jeśli chcesz *SomeFieldbyć inny niż 0, potrzebujesz czegoś adresowalnego.

Nadal możesz to zrobić, ale to brzydkie:

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

To, co się tutaj dzieje, to []int64plasterek tworzony za pomocą literału, który ma jeden element ( 5). I jest indeksowany (element zerowy) i brany jest adres elementu zerowego. W tle tablica [1]int64zostanie również przydzielona i użyta jako tablica zapasowa dla wycinka. Jest tu więc dużo schematów.

6) Ze strukturą pomocniczą dosłownie

Przeanalizujmy wyjątek od wymagań adresowalności:

Jako wyjątek od wymagania adresowalności, x[w wyrażeniu &x] może być również (prawdopodobnie umieszczonym w nawiasach) literałem złożonym .

Oznacza to, że pobranie adresu literału złożonego, np. Literału struktury, jest w porządku. Jeśli to zrobimy, będziemy mieli przydzieloną wartość struktury i uzyskany do niej wskaźnik. Ale jeśli tak, stanie się dla nas dostępne inne wymaganie: „selektor pola argumentu struktury adresowalnej” . Więc jeśli literał struct zawiera pole typu int64, możemy również wziąć adres tego pola!

Zobaczmy, jak działa ta opcja. Użyjemy tego typu struktury otoki:

type intwrapper struct {
    x int64
}

A teraz możemy:

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

Zauważ, że this

&(&intwrapper{6}).x

oznacza co następuje:

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

Możemy jednak pominąć „zewnętrzny” nawias, ponieważ operator adresu &jest stosowany do wyniku wyrażenia selektora .

Zwróć również uwagę, że w tle wydarzy się co następuje (jest to również poprawna składnia):

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

7) Z helper anonymous struct dosłownie

Zasada jest taka sama jak w przypadku # 6, ale możemy również użyć anonimowego literału struktury, więc nie jest potrzebna definicja typu helper / wrapper struct:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
źródło
1
różni się to funkcjonalnie od ostatniego przykładu w pytaniu z symbolem zastępczym, ponieważ instancedwa różne obiekty wskazywałyby na dwa różne int64s. Ale wydaje się, że odpowiednio spełnia intencje OP
Ryan Haining
2
to zdecydowanie odpowiada na moje pytanie. Ale denerwuje mnie to :,&[]int64{2}[0] czuję, że na podstawie opisu stałych na blog.golang.org/constants powinno to działać tak, jak &0.
ThisGuy
@RyanHaining A co by się stało, gdyby działało tak, jakby został przypisany ten sam adres? Gdyby dwa różne instanceobiekty wskazywały na ten sam int64i jeden zmodyfikowałby wskazany obiekt, oba uległyby zmianie. A co, jeśli teraz użyjesz tego samego literału do utworzenia trzeciego instance? Ten sam adres wskazywałby teraz na inną int64wartość ... więc należy użyć innego adresu, ale w takim razie po co używać tego samego w przypadku pierwszych 2?
icza
@icza zazwyczaj nie chciałbyś, aby wskazywały na ten sam obiekt, nie mówię, że tak. Po prostu wskazuję na różnicę.
Ryan Haining
4
Stałe @Conslo są oceniane w czasie kompilacji. Prawidłowa wartość wskaźnika, prawidłowy adres pamięci istnieje tylko w czasie wykonywania, więc nie jest to to samo, co stałe.
icza
5

Użyj funkcji, która zwraca adres zmiennej int64, aby rozwiązać problem.

W poniższym kodzie używamy funkcji, fktóra akceptuje liczbę całkowitą i zwraca wartość wskaźnika, która przechowuje adres liczby całkowitej. Tą metodą możemy łatwo rozwiązać powyższy problem.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
ASHWIN RAJEEV
źródło