Any nie je Any, ale Any je Any
Any
V jazyku Go je typ any
aliasom pre čarovné rozhranie interface{}
, ktoré dokáže pojať akýkoľvek typ. To znamená, že akýkoľvek typ môže byť priradený premennej typu any
.
1var a any
2a = 42 // int
3a = "hello" // string
4a = true // bool
5a = 3.14 // float64
Ako je vidieť, typ any
umožňuje písať flexibilný kód tým, že dokáže pojať hodnoty rôznych typov. Pri používaní typu any
je však potrebné zvážiť niekoľko vecí.
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}
V uvedenom príklade a.(string)
potvrdzuje, že a
je typu string. Ak a
nie je typu string, ok
bude false, čím sa zabráni panike programu.
Type Switch
Type switch
umožňuje vykonávať rôzne akcie 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}
V uvedenom príklade a.(type)
kontroluje skutočný typ a
a vykoná príslušný case.
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())
V uvedenom príklade je možné pomocou balíka reflect
počas behu overiť typ a hodnotu a
.
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ým sme sa zaoberali doteraz.
Štruktúra
EFace je špeciálna štruktúra, ktorá existuje iba v rámci runtime jazyka Go, takže 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 má dve polia:
Type
: Ukazovateľ obsahujúci metadáta o skutočnom type hodnoty.Data
: Ukazovateľ obsahujúci skutočnú hodnotu.
EFace interne spravuje informácie o type prostredníctvom štruktúry runtimeType
. Táto štruktúra obsahuje rôzne metadáta, ako je veľkosť, zarovnanie a hash hodnota typu.
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.
Obalenie
Teraz obalíme tento EFace, aby sme mohli ľahšie vykoná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 je možné pomocou TypeInfo
ľahko pristupovať k hash hodnote, druhu a porovnávacej funkcii typu.
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}
V uvedenom príklade sa celočíselné a desatinné hodnoty odovzdávajú funkcii GetTypeInfo
na výstup ich príslušných informácií o type a hodnoty sa porovnávajú pomocou funkcie Equal
. To umožňuje rýchlejší prístup k informáciám o type a porovnávanie typov.
Okrem toho je tento prístup použiteľný aj pre štruktúry, ukazovatele a rozhrania a dokáže vyriešiť problém „typed nil“, ktorý sa často vyskytuje. Hash
bude priradený, ak existuje typ, ale This
bude 0, ak je nil.
Záver
EFace v súčasnosti hrá dôležitú ú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 rámci runtime, ktorá môže byť ovplyvnená zmenami špecifikácie jazyka, preto je potrebná opatrnosť.