Bagaimana cara melakukan * int64 literal di Go?

111

Saya memiliki tipe struct dengan *int64bidang.

type SomeType struct {
    SomeField *int64
}

Di beberapa titik dalam kode saya, saya ingin menyatakan literal ini (katakanlah, ketika saya tahu nilai tersebut harus 0, atau menunjuk ke 0, Anda tahu apa yang saya maksud)

instance := SomeType{
    SomeField: &0,
}

... kecuali ini tidak berhasil

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

Jadi saya coba ini

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

... tapi ini juga tidak berhasil

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

Bagaimana saya melakukan ini? Satu-satunya solusi yang bisa saya dapatkan adalah menggunakan variabel placeholder

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Catatan: &0sintaksnya berfungsi dengan baik jika berupa * int, bukan *int64. Sunting: tidak. Maaf soal ini.

Edit:

Rupanya, pertanyaan saya terlalu ambigu. Saya sedang mencari cara untuk menyatakan a *int64. Ini bisa digunakan di dalam konstruktor, atau untuk menyatakan nilai struct literal, atau bahkan sebagai argumen untuk fungsi lain. Tapi fungsi pembantu atau menggunakan tipe yang berbeda bukanlah solusi yang saya cari.

Orang ini
sumber
1
Pointer ke int sangat disayangkan karena int mengambil jumlah ruang yang sama dengan pointer, jadi Anda tidak menghemat ruang. Itu hanya menambahkan nilai NULL yang biasanya hanya lebih kompleks daripada nilainya. Dalam kebanyakan kasus, angka 0 sudah cukup. Jika Anda memerlukan nilai tambahan, bool "IsValidSomeField" juga berfungsi dan jika Anda memberi nama yang lebih baik pada bool itu, ia dapat menjelaskan lebih banyak tentang mengapa Anda membutuhkan nilai ekstra itu, yang bagus untuk keterbacaan.
voutasaurus
3
Anda dapat menggunakan penunjuk paket , misalnya:var _ *int64 = pointer.Int64(64)
Xor
Sayang sekali kami harus menulis fungsi atau bahkan perpustakaan untuk dapat melakukan ini.
Kelapa

Jawaban:

224

Go Language Specification ( operator Alamat ) tidak memungkinkan untuk mengambil alamat konstan numerik (bukan dari sebuah untyped atau dari diketik konstan).

Operand harus dapat dialamatkan , yaitu, variabel, petunjuk arah pointer, atau operasi pengindeksan slice; atau pemilih bidang dari operan struct beralamat; atau operasi pengindeksan array dari array yang dapat dialamatkan. Sebagai pengecualian untuk persyaratan pengalamatan, x[dalam ekspresi &x] mungkin juga merupakan literal komposit (mungkin dalam tanda kurung) .

Untuk alasan mengapa hal ini tidak diperbolehkan, lihat pertanyaan terkait: Temukan alamat konstanta di jalan . Pertanyaan serupa (juga tidak diizinkan untuk mengambil alamatnya): Bagaimana cara menyimpan referensi ke hasil operasi di Go?

Pilihan Anda (coba semua di Go Playground ):

1) Dengan new()

Anda cukup menggunakan new()fungsi bawaan untuk mengalokasikan nilai nol baru int64dan mendapatkan alamatnya:

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

Tetapi perhatikan bahwa ini hanya dapat digunakan untuk mengalokasikan dan mendapatkan penunjuk ke nilai nol jenis apa pun.

2) Dengan variabel pembantu

Paling sederhana dan direkomendasikan untuk elemen bukan nol adalah dengan menggunakan variabel pembantu yang alamatnya dapat diambil:

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

3) Dengan fungsi pembantu

Catatan: Fungsi pembantu untuk memperoleh penunjuk ke nilai bukan nol tersedia di github.com/icza/goxperpustakaan saya , dalam goxpaket, jadi Anda tidak perlu menambahkan ini ke semua proyek Anda di mana Anda membutuhkannya.

Atau jika Anda membutuhkan ini berkali-kali, Anda dapat membuat fungsi helper yang mengalokasikan dan mengembalikan *int64:

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

Dan menggunakannya:

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

Perhatikan bahwa kami sebenarnya tidak mengalokasikan apa pun, kompilator Go melakukannya ketika kami mengembalikan alamat argumen fungsi. Kompilator Go melakukan analisis escape, dan mengalokasikan variabel lokal pada heap (bukan stack) jika variabel tersebut dapat lolos dari fungsi. Untuk detailnya, lihat Apakah mengembalikan sepotong larik lokal dalam fungsi Go aman?

4) Dengan fungsi anonim satu baris

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

Atau sebagai alternatif (lebih pendek):

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

5) Dengan slice literal, indexing dan taking address

Jika Anda ingin *SomeFieldmenjadi selain 0, maka Anda membutuhkan sesuatu yang dapat dialamatkan.

Anda masih bisa melakukannya, tapi itu jelek:

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

Apa yang terjadi di sini adalah []int64irisan dibuat dengan literal, memiliki satu elemen ( 5). Dan itu diindeks (elemen ke-0) dan alamat elemen ke-0 diambil. Di latar belakang, sebuah array [1]int64juga akan dialokasikan dan digunakan sebagai backing array untuk slice. Jadi ada banyak boilerplate di sini.

6) Dengan bantuan struct literal

Mari kita periksa pengecualian untuk persyaratan kemampuan alamat:

Sebagai pengecualian untuk persyaratan pengalamatan, x[dalam ekspresi &x] mungkin juga merupakan literal komposit (mungkin dalam tanda kurung) .

Ini berarti bahwa mengambil alamat literal komposit, misalnya struct literal tidak masalah. Jika kita melakukannya, kita akan memiliki nilai struct yang dialokasikan dan pointer yang diperoleh. Namun jika demikian, persyaratan lain akan tersedia untuk kita: "pemilih bidang dari operand struct beralamat" . Jadi jika struct literal berisi sebuah field dari tipe int64, kita juga dapat mengambil alamat dari field tersebut!

Mari kita lihat opsi ini beraksi. Kami akan menggunakan tipe struct pembungkus ini:

type intwrapper struct {
    x int64
}

Dan sekarang kita bisa melakukan:

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

Perhatikan bahwa ini

&(&intwrapper{6}).x

berarti sebagai berikut:

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

Tapi kita bisa menghilangkan tanda kurung "luar" karena operator alamat &diterapkan ke hasil ekspresi selektor .

Perhatikan juga bahwa di latar belakang hal berikut akan terjadi (ini juga merupakan sintaks yang valid):

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

7) Dengan pembantu anonymous struct literal

Prinsipnya sama dengan kasus # 6, tetapi kita juga dapat menggunakan literal struct anonim, jadi tidak perlu definisi tipe struct helper / wrapper:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
sumber
1
ini secara fungsional berbeda dari contoh terakhir dalam pertanyaan dengan placeholder, karena dua instanceobjek yang berbeda akan menunjuk ke dua yang berbeda int64. Tapi tampaknya itu memenuhi maksud OP secara memadai
Ryan Haining
2
ini pasti menjawab pertanyaan saya. Tapi itu membuat saya sangat kesal :,&[]int64{2}[0] saya merasa berdasarkan deskripsi konstanta di blog.golang.org/constants ini seharusnya berfungsi sebagai &0.
ThisGuy
@RyanHaining Dan apa yang akan terjadi jika itu akan bekerja seperti alamat yang sama akan diberikan? Jika dua instanceobjek yang berbeda akan menunjuk ke yang sama int64dan satu akan memodifikasi objek yang ditunjuk, keduanya akan berubah. Dan bagaimana jika sekarang Anda menggunakan literal yang sama untuk membuat yang ketiga instance? Alamat yang sama sekarang akan menunjuk ke int64nilai yang berbeda ... jadi alamat yang berbeda harus digunakan, tapi mengapa menggunakan yang sama dalam kasus 2 yang pertama?
icza
@icza Anda biasanya tidak ingin mereka menunjuk ke objek yang sama, saya tidak mengatakan Anda akan melakukannya. Saya hanya menunjukkan perbedaannya.
Ryan Haining
4
Konstanta @Conslo dievaluasi pada waktu kompilasi. Nilai pointer yang valid, alamat memori yang valid hanya ada saat runtime, jadi ini tidak sama dengan konstanta.
icza
5

Gunakan fungsi yang mengembalikan alamat variabel int64 untuk menyelesaikan masalah.

Dalam kode di bawah ini kami menggunakan fungsi fyang menerima integer dan mengembalikan nilai pointer yang menyimpan alamat integer. Dengan menggunakan metode ini kita dapat dengan mudah menyelesaikan masalah di atas.

type myStr struct {
    url *int64
}

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