GoSuda

Any bukanlah Any, tetapi Any adalah Any

By snowmerak
views ...

Any

Dalam bahasa Go, tipe any adalah alias untuk antarmuka "ajaib", interface{}, yang dapat menampung semua tipe. Dengan kata lain, tipe apa pun dapat ditetapkan ke variabel bertipe any.

1var a any
2a = 42          // int
3a = "hello"     // string
4a = true        // bool
5a = 3.14        // float64

Seperti yang ditunjukkan, tipe any dapat menampung nilai dari berbagai tipe, memungkinkan penulisan kode yang fleksibel. Namun, ada beberapa hal yang perlu diperhatikan saat menggunakan tipe any.

Type Assertion

Untuk menggunakan nilai sebenarnya dari variabel bertipe any, Anda harus menggunakan type assertion. Type assertion memberitahu kompiler bahwa variabel bertipe any adalah tipe tertentu.

1var a any = "hello"
2str, ok := a.(string)
3if ok {
4    fmt.Println("String value:", str)
5} else {
6    fmt.Println("Not a string")
7}

Dalam contoh di atas, a.(string) mengklaim bahwa a adalah tipe string. Jika a bukan tipe string, ok akan menjadi false, dan ini dapat mencegah program mengalami panik.

Type Switch

Type switch memungkinkan tindakan yang berbeda dilakukan berdasarkan tipe sebenarnya dari variabel bertipe any. Ini berguna saat perlu menangani beberapa tipe.

 1var a any = 42
 2switch v := a.(type) {
 3case int:
 4    fmt.Println("Integer:", v)
 5case string:
 6    fmt.Println("String:", v)
 7case bool:
 8    fmt.Println("Boolean:", v)
 9default:
10    fmt.Println("Unknown type")
11}

Dalam contoh di atas, a.(type) memeriksa tipe sebenarnya dari a dan menjalankan case yang sesuai dengan tipe tersebut.

Reflection

Menggunakan paket reflect bahasa Go, tipe dan nilai variabel bertipe any dapat diperiksa dan dimanipulasi secara dinamis saat runtime. Ini berguna saat menangani struktur data yang kompleks.

1import (
2    "fmt"
3    "reflect"
4)
5
6var a any = 3.14
7v := reflect.ValueOf(a)
8fmt.Println("Type:", v.Type())
9fmt.Println("Value:", v.Float())

Dalam contoh di atas, paket reflect digunakan untuk memverifikasi tipe dan nilai a saat runtime.

EFace

Dalam bahasa Go, antarmuka dibagi menjadi dua bentuk utama: EFace (Empty Interface) dan IFace (Interface with Methods). Di antaranya, EFace adalah konsep yang sama dengan tipe any yang telah kami bahas sejauh ini.

Struktur

EFace adalah struktur khusus yang hanya ada di dalam runtime bahasa Go, jadi di sini kita akan membuat mimic untuk menanganinya.

 1type runtimeTypeMimic struct {
 2	Size_       uintptr
 3	PtrBytes    uintptr // number of (prefix) bytes in the type that can contain pointers
 4	Hash        uint32  // hash of type; avoids computation in hash tables
 5	TFlag       uint8   // extra type information flags
 6	Align_      uint8   // alignment of variable with this type
 7	FieldAlign_ uint8   // alignment of struct field with this type
 8	Kind_       uint8   // enumeration for C
 9	Equal       func(unsafe.Pointer, unsafe.Pointer) bool
10	GCData      *byte
11	Str         int32 // string form
12	PtrToThis   int32 // type for pointer to this type, may be zero
13}
14
15type eFaceMimic struct {
16	Type *runtimeTypeMimic
17	Data unsafe.Pointer
18}

EFace memiliki dua bidang:

  • Type: sebuah pointer yang berisi metadata tentang tipe sebenarnya dari nilai.
  • Data: sebuah pointer yang berisi nilai sebenarnya.

Dan EFace secara internal mengelola informasi tipe melalui struktur runtimeType. Struktur ini mencakup berbagai metadata seperti ukuran tipe, penyelarasan, dan nilai hash.

Bagian yang harus kita perhatikan di sini adalah bidang Kind_, Hash, dan Equal. Kind_ menunjukkan jenis tipe, dan Hash serta Equal digunakan untuk membandingkan tipe dan menghitung nilai hash dalam tabel hash.

Pembungkus

Sekarang, mari kita bungkus EFace ini untuk membuatnya lebih mudah untuk melakukan sihir hitam.

 1type TypeInfo struct {
 2	Hash  uint32
 3	TFlag uint8
 4	Kind  uint8
 5	Equal func(unsafe.Pointer, unsafe.Pointer) bool
 6	This  unsafe.Pointer
 7}
 8
 9func GetTypeInfo(v any) TypeInfo {
10	eface := *(*eFaceMimic)(unsafe.Pointer(&v))
11	return TypeInfo{
12		Hash:  eface.Type.Hash,
13		TFlag: eface.Type.TFlag,
14		Kind:  eface.Type.Kind_,
15		Equal: eface.Type.Equal,
16		This:  eface.Data,
17	}
18}

Fungsi di atas menerima variabel bertipe any, mengekstrak struktur EFace internalnya, dan mengambil informasi tipe darinya untuk dikembalikan sebagai struktur TypeInfo. Sekarang, menggunakan TypeInfo ini, nilai hash tipe, jenis, fungsi perbandingan, dan lainnya dapat diakses dengan mudah.

Pemanfaatan

Pemanfaatannya sangat mudah. Cukup berikan variabel bertipe any ke fungsi GetTypeInfo.

 1func main() {
 2	i := 42
 3	eface := GetTypeInfo(i)
 4	fmt.Printf("%+v\n", eface)
 5
 6	f := 3.14
 7	eface2 := GetTypeInfo(f)
 8	fmt.Printf("%+v\n", eface2)
 9
10	// Compare the two values using the Equal function from the type info
11	log.Println(eface.Equal(eface.This, eface.This))
12	log.Println(eface2.Equal(eface2.This, eface2.This))
13	log.Println(eface2.Equal(eface.This, eface2.This))
14	log.Println(eface.Equal(eface2.This, eface.This))
15}

Dalam contoh di atas, nilai integer dan floating-point diberikan ke fungsi GetTypeInfo untuk mencetak informasi tipe masing-masing, dan nilai-nilai tersebut dibandingkan menggunakan fungsi Equal. Melalui ini, informasi tipe dapat diperoleh lebih cepat, dan tipe dapat dibandingkan.

Selain itu, metode ini dapat digunakan secara identik untuk struktur, pointer, dan antarmuka, dan juga dapat menyelesaikan masalah nil bertipe (typed nil) yang sering terjadi. Ini karena Hash akan dialokasikan jika ada tipe, tetapi This akan menjadi 0 jika nil.

Kesimpulan

EFace saat ini memainkan peran penting dalam menangani tipe any dalam bahasa Go. Melaluinya, nilai-nilai dari berbagai tipe dapat diproses secara fleksibel dan cepat, serta informasi tipe dapat diperoleh dan dimanfaatkan secara rinci. Namun, karena ini adalah fungsi yang tersembunyi di dalam runtime, perlu diperhatikan bahwa ini dapat terpengaruh oleh perubahan spesifikasi bahasa.