Any nie je Any, ale Any je Any
Any
V jazyku Go je typ any aliasom pre magický interface interface{}, ktorý dokáže obsiahnuť akýkoľvek typ. To znamená, že do premennej typu any možno priradiť akýkoľvek typ.
1var a any
2a = 42 // int
3a = "hello" // string
4a = true // bool
5a = 3.14 // float64
Takto typ any umožňuje písať flexibilný kód, pretože dokáže obsiahnuť hodnoty rôznych typov. Pri používaní typu any je však potrebné zvážiť niekoľko bodov.
Type Assertion
Na použitie skutočnej hodnoty z premennej typu any je potrebné použiť type assertion. Type assertion informuje kompilátor, že premenná typu any je konkrétneho typu.
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}
Vo vyššie uvedenom príklade a.(string) potvrdzuje, že a je typu string. Ak a nie je typu string, ok bude false, čím sa predíde panike programu.
Type Switch
Type switch umožňuje vykonávať rôzne operácie v závislosti od skutočného typu premennej typu any. To je užitočné pri spracovaní viacerých typov.
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}
Vo vyššie uvedenom príklade a.(type) kontroluje skutočný typ a a vykoná príslušný prípad.
Reflection
Balík reflect v jazyku Go umožňuje dynamicky kontrolovať a manipulovať s typom a hodnotou premennej typu any počas behu. To je užitočné pri práci so zložitými dátovými štruktúrami.
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())
Vo vyššie uvedenom príklade možno pomocou balíka reflect zistiť typ a hodnotu a počas behu.
EFace
V jazyku Go sa rozhrania delia na dve hlavné formy: EFace (Empty Interface) a IFace (Interface with Methods). EFace je rovnaký koncept ako typ any, ktorý sme doteraz preberali.
Štruktúra
EFace je špeciálna štruktúra, ktorá existuje len v rámci runtime jazyka Go, preto tu vytvoríme jej napodobeninu (mimic).
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 obsahuje dve polia:
Type: Ukazovateľ obsahujúci metadáta o skutočnom type hodnoty.Data: Ukazovateľ obsahujúci skutočnú hodnotu.
Internne EFace spravuje informácie o type prostredníctvom štruktúry runtimeType. Táto štruktúra obsahuje rôzne metadáta, ako je veľkosť typu, zarovnanie, hash hodnota atď.
V tejto časti by sme sa mali zamerať na polia Kind_, Hash a Equal. Kind_ predstavuje druh typu a Hash a Equal sa používajú na porovnávanie typov a výpočet hash hodnôt v hash tabuľkách.
Zapuzdrenie
Teraz zabalíme tento EFace, aby sme mohli ľahšie používať „čiernu mágiu“.
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}
Vyššie uvedená funkcia prijíma premennú typu any, extrahuje internú štruktúru EFace a z nej vyberie informácie o type, ktoré vráti ako štruktúru TypeInfo. Teraz možno pomocou tejto TypeInfo ľahko pristupovať k hash hodnote typu, druhu a porovnávacej funkcii.
Použitie
Použitie je skutočne jednoduché. Stačí odovzdať premennú typu any funkcii 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}
Vo vyššie uvedenom príklade sa celočíselná a desatinná hodnota odovzdajú funkcii GetTypeInfo, aby sa vytlačili informácie o ich type, a potom sa hodnoty porovnajú pomocou funkcie Equal. To umožňuje rýchlejšie získavanie informácií o type a porovnávanie typov.
Okrem toho možno tento prístup použiť aj pre štruktúry, ukazovatele a rozhrania, a dokáže vyriešiť aj častý problém s „typed nil“ (nil s typom). Zatiaľ čo Hash bude pridelený, ak existuje typ, This bude 0, ak je nil.
Záver
EFace v súčasnosti hrá kľúčovú úlohu pri spracovaní typu any v jazyku Go. Umožňuje flexibilné a rýchle spracovanie hodnôt rôznych typov a poskytuje podrobné informácie o type, ktoré možno využiť. Je však potrebné poznamenať, že ide o skrytú funkciu v runtime, ktorá môže byť ovplyvnená zmenami v špecifikácii jazyka.