GoSuda

Any není Any, ale Any je Any.

By snowmerak
views ...

Any

V jazyce Go je typ any aliasem pro magický interface interface{}, který může pojmout jakýkoli typ. To znamená, že jakýkoli typ může být přiřazen proměnné typu any.

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

Jak je vidět, typ any umožňuje psát flexibilní kód, neboť může obsahovat hodnoty různých typů. Nicméně, při používání typu any je třeba mít na paměti několik důležitých bodů.

Type Assertion

Pro použití skutečné hodnoty z proměnné typu any je nutné použít type assertion. Type assertion informuje kompilátor, že proměnná typu any je určitého 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 uvedeném příkladu a.(string) tvrdí, že a je typu string. Pokud by a nebylo typu string, ok by bylo false a program by se zabránil panice.

Type Switch

Type switch umožňuje provádět různé akce v závislosti na skutečném typu proměnné typu any. To je užitečné při zpracování více typů.

 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 uvedeném příkladu a.(type) kontroluje skutečný typ a a provede odpovídající případ.

Reflection

Pomocí balíčku reflect v jazyce Go je možné dynamicky kontrolovat a manipulovat s typem a hodnotou proměnné typu any za běhu. To je užitečné při práci se složitými datovými strukturami.

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 uvedeném příkladu lze pomocí balíčku reflect ověřit typ a hodnotu a za běhu.

EFace

V jazyce Go jsou interface rozděleny do dvou hlavních forem: EFace (Empty Interface) a IFace (Interface with Methods). Z nich je EFace shodný s konceptem typu any, který jsme dosud probírali.

Struktura

EFace je speciální struktura, která existuje pouze v rámci runtime jazyka Go, proto zde vytvoříme její imitaci.

 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 se skládá ze dvou polí:

  • Type: ukazatel obsahující metadata o skutečném typu hodnoty.
  • Data: ukazatel obsahující skutečnou hodnotu.

Interně EFace spravuje informace o typu prostřednictvím struktury runtimeType. Tato struktura obsahuje různá metadata, jako je velikost typu, zarovnání, hash hodnota atd.

V této části bychom se měli zaměřit na pole Kind_, Hash a Equal. Kind_ představuje druh typu a Hash a Equal se používají k porovnávání typů a výpočtu hash hodnot v hash tabulkách.

Zabalení

Nyní zabalíme tento EFace, abychom mohli snáze používat "černou magii".

 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}

Výše uvedená funkce přijímá proměnnou typu any, extrahuje interní strukturu EFace a z ní získává informace o typu, které vrací jako strukturu TypeInfo. Nyní lze pomocí TypeInfo snadno přistupovat k hash hodnotě typu, jeho druhu, porovnávací funkci atd.

Využití

Využití je skutečně snadné. Stačí předat proměnnou typu any funkci 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 uvedeném příkladu jsou celočíselné a desetinné hodnoty předány funkci GetTypeInfo pro výstup informací o jejich typech a funkce Equal je použita k porovnání hodnot. To umožňuje rychleji získávat informace o typech a porovnávat je.

Tato metoda může být také použita pro struktury, ukazatele a interface, a může řešit problém typed nil, který se poměrně často vyskytuje. Hash bude přidělen, pokud existuje typ, ale This bude 0, pokud je nil.

Závěr

EFace hraje v současném jazyce Go důležitou roli při manipulaci s typem any. Umožňuje flexibilní a rychlé zpracování hodnot různých typů a získávání a využívání podrobných informací o typech. Je však třeba poznamenat, že se jedná o funkci skrytou v runtime, která může být ovlivněna změnami ve specifikaci jazyka.