Pertanyaan dan konteks
Go 1.26 memperluas fungsi bawaan new sehingga dapat menerima sebuah ekspresi dan mengembalikan pointer ke variabel baru. Gunakan struct API untuk menunjukkan bagaimana hal ini menyederhanakan inisialisasi field opsional, kemudian bahas JSON, generics, kompilator versi lama, dan batasan review.
Hal yang dievaluasi oleh pewawancara
- Apakah Anda membedakan
new(T), pengambilan alamat, dan evaluasi ekspresi. - Apakah Anda menjelaskan pointer opsional dengan zero value,
omitempty, dan unmarshalling. - Apakah Anda mengidentifikasi risiko versi Go, tipe generic, dan keterbacaan.
- Apakah Anda menyediakan pengujian dan rencana migrasi, bukan hanya sekadar sintaksis.
Pertanyaan klarifikasi terlebih dahulu
Kontrak data
Apakah API harus membedakan antara field yang hilang (missing), zero value eksplisit, dan null eksplisit? Apakah klien JSON mengandalkan omitempty? Apakah database juga memerlukan semantik tiga status (three-state)?
Versi dan deployment
Apakah semua lingkungan build telah beralih ke Go 1.26? Apakah pustaka bersama (shared libraries), generator, atau modul hilir masih menggunakan toolchain yang lebih lama?
Keterbacaan dan kebijakan
Apakah tim mengizinkan ekspresi kompleks sebagai nilai pointer dalam kode bisnis? Haruskah fungsi helper sederhana tetap dipertahankan untuk debugging dan review?
Jawaban 30 detik
Go 1.26 memungkinkan new(expr) menempatkan hasil ekspresi ke dalam variabel baru dan mengembalikan pointernya, yang sangat praktis untuk field opsional seperti new(42). Pertama-tama saya akan memutuskan apakah kontraknya membedakan missing, zero, dan null, lalu memilih pointer atau tipe opsional khusus. Sebelum migrasi, kunci versi toolchain, tambahkan pengujian serialisasi, dan jaga agar ekspresi tetap pendek demi keterbacaan.
Solusi mendalam
1. Jelaskan perubahan semantik
new(T) tradisional menerima sebuah tipe dan mengembalikan pointer ke zero value-nya. Go 1.26 mengizinkan operan berupa ekspresi: kompilator membuat variabel, menyimpan hasil ekspresi, dan mengembalikan pointernya. Ekspresi tersebut tetap mengikuti aturan evaluasi normal Go, sehingga efek samping (side effects) dan urutan eksekusi harus tetap terlihat jelas.
2. Memodelkan field opsional
Struct berikut menggunakan pointer untuk menyatakan apakah suatu field diberikan:
type CreateUser struct {
Name string `json:"name"`
Age *int `json:"age,omitempty"`
Admin *bool `json:"admin,omitempty"`
}
req := CreateUser{
Name: "Ada",
Age: new(37),
Admin: new(false),
}Admin: new(false) berbeda secara semantik dari Admin: nil. Apakah false dimunculkan atau tidak bergantung pada aturan serialisasi; pointer non-nil umumnya tetap ada meskipun nilai yang ditunjuknya adalah zero value di bawah omitempty, jadi ujilah kontrak API tersebut.
3. Membandingkan alternatif
new(37) lebih ringkas, sedangkan age := 37; &age lebih mudah diperiksa dalam debugger. Helper ptrToInt(37) dapat memusatkan kompatibilitas dengan toolchain yang lebih lama. Pilihlah berdasarkan kebijakan tim, kompleksitas ekspresi, dan frekuensi pemanggilan; jangan menulis ulang setiap inisialisasi pointer hanya untuk menggunakan sintaksis baru.
4. Menangani generics dan inferensi
Tipe hasil dari new(expr) mengikuti tipe ekspresi. Ekspresi generic yang kompleks dapat mempersulit inferensi bagi pembaca kode. Pustaka publik harus menuliskan tipe secara eksplisit pada batas-batasnya (boundaries); kode internal harus menggunakan konstanta sederhana atau variabel bernama dan membiarkan kompilator serta pemeriksaan statis menangkap kesalahan tipe.
5. Memperhatikan batasan JSON dan database
Pointer membedakan nil dari non-nil tetapi tidak menyelesaikan semua pembedaan antara NULL database, string kosong, dan zero. Untuk API serupa PATCH, uji kontrak secara eksplisit: missing berarti tidak ada pembaruan, zero non-nil berarti mengosongkan nilai, dan null diterima atau ditolak oleh lapisan decoding.
6. Merencanakan migrasi kompatibilitas
Selaraskan go.mod, CI, image kontainer, dan langkah-langkah pembuatan kode pada Go 1.26. Jika pengguna hilir masih mengompilasi dengan toolchain yang lebih lama, pertahankan bentuk lama atau pisahkan kode baru menggunakan matriks build. Migrasikan paket kecil terlebih dahulu, lalu jalankan pengujian unit, serialisasi, dan race condition sebelum memperluasnya.
7. Menetapkan aturan review
Izinkan ekspresi pendek dan bebas efek samping di dalam new; beri nama pada komputasi yang kompleks terlebih dahulu. Review harus berfokus pada semantik tiga status field, masa hidup pasca-escape, penanganan error, dan kompatibilitas API, bukan hanya memeriksa apakah sintaksisnya dapat dikompilasi.
Contoh jawaban yang kuat
new(expr) pada Go 1.26 sangat praktis untuk mengubah ekspresi sederhana menjadi pointer field opsional, tetapi tidak mengubah pointer, JSON, atau semantik tiga status database. Saya akan menggunakan *T untuk membedakan missing dari zero eksplisit, menulis pengujian kontrak PATCH dan serialisasi, serta bermigrasi dengan satu toolchain yang versinya dikunci. Ekspresi kompleks tetap menggunakan variabel bernama, dan jalur toolchain lama tetap tersedia sehingga keterbacaan dan kompatibilitas merupakan pilihan yang disengaja.
Kesalahan umum
- Memperlakukan
new(0)sebagai nil meskipun ia mengembalikan pointer non-nil. - Menganggap
omitemptysecara otomatis menghapus pointer ke zero value. - Melakukan commit sintaksis Go 1.26 sebelum memperbarui CI atau toolchain hilir.
- Menggunakan pointer tanpa mendefinisikan semantik missing, null, dan zero untuk PATCH.
- Menempatkan ekspresi kompleks yang memiliki efek samping di dalam
new. - Hanya menjalankan pemeriksaan kompilasi dan melewatkan pengujian batasan JSON serta database.
Pertanyaan lanjutan dan jawaban
Apa perbedaan antara new(0) dan new(int)?
new(0) mengembalikan pointer ke int yang nilainya 0; new(int) mengembalikan pointer ke zero value int. Nilainya sama, tetapi yang pertama menunjukkan penggunaan operan ekspresi Go 1.26.
Mengapa tidak menggunakan value field dengan nilai default?
Value field tidak dapat membedakan antara field yang hilang (missing) dan zero eksplisit. Untuk PATCH atau kompatibilitas dengan klien lama, pointer atau tipe opsional khusus mengekspresikan kontrak dengan lebih baik.
Apakah ini mengubah analisis escape (escape analysis)?
Kompilator tetap memutuskan apakah variabel ditempatkan di stack atau heap. Diskusikan semantik yang dapat diamati dan benchmark; jangan menyimpulkan alokasi heap hanya dari kata kunci new.
Bagaimana cara mendukung versi Go yang lebih lama?
Tetapkan versi minimum secara konsisten dalam modul, CI, dan image rilis. Jika peningkatan serentak tidak memungkinkan, pertahankan v := value; &v atau fungsi helper dan gunakan matriks build untuk menjaga sintaksis baru tidak masuk ke cabang lama.
Haruskah setiap field beralih ke new(expr)?
Tidak. Gunakan jika itu membuat inisialisasi opsional menjadi lebih jelas. Ekspresi kompleks, API publik, atau kode yang memerlukan breakpoint debugger dapat tetap menggunakan variabel bernama.