GoSuda

Any nie je Any, ale Any je Any

By snowmerak
views ...

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.