Any není Any, ale Any je Any
Any
V jazyce Go je typ any
aliasem pro magický interface interface{}
, který dokáže pojmout jakýkoli typ. To znamená, že jakýkoli typ lze přiřadit proměnné typu any
.
1var a any
2a = 42 // int
3a = "hello" // string
4a = true // bool
5a = 3.14 // float64
Typ any
tak umožňuje psát flexibilní kód, který dokáže pracovat s různými typy hodnot. Při použití typu any
je však třeba mít na paměti několik věcí.
Type Assertion
Chcete-li použít skutečnou hodnotu z proměnné typu any
, musíte použít tzv. 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 tomto příkladu a.(string)
tvrdí, že a
je typu string
. Pokud a
není typu string
, ok
bude false
a program se tak vyhne panice.
Type Switch
Type switch umožňuje provádět různé akce v závislosti na skutečném typu proměnné any
. To je užitečné, když potřebujete zpracovat 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 tomto příkladu a.(type)
kontroluje skutečný typ a
a spustí odpovídající case
.
Reflection
Pomocí balíčku reflect
v jazyce Go můžete dynamicky kontrolovat a manipulovat s typem a hodnotou proměnné typu any
za běhu programu. 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 tomto příkladu lze pomocí balíčku reflect
za běhu programu zkontrolovat typ a hodnotu a
.
EFace
V jazyce Go se rozlišují dva hlavní typy rozhraní: EFace (Empty Interface) a IFace (Interface with Methods). EFace je stejný koncept jako typ any
, kterým jsme se dosud zabývali.
Struktura
EFace je speciální struktura, která existuje pouze v rámci runtime jazyka Go, proto zde vytvoříme její mimikry.
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á dvě pole:
Type
: ukazatel obsahující metadata o skutečném typu hodnoty.Data
: ukazatel obsahující skutečnou hodnotu.
Interně EFace spravuje informace o typech prostřednictvím struktury runtimeType
. Tato struktura zahrnuje různá metadata, jako je velikost typu, zarovnání a hodnota hash.
V této struktuře si všimneme polí Kind_
, Hash
a Equal
. Kind_
označuje druh typu, zatímco Hash
a Equal
se používají k porovnávání typů a výpočtu hash hodnot v hash tabulkách.
Obalení
Nyní zabalíme tento EFace, abychom mohli pohodlněji 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 z ní vnitřní strukturu EFace a z ní získává informace o typu, které vrací jako strukturu TypeInfo
. Nyní lze pomocí této TypeInfo
snadno přistupovat k hash hodnotě, druhu a porovnávací funkci typu.
Použití
Použití je velmi jednoduché. 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 tomto příkladu jsou celočíselné a desetinné hodnoty předány funkci GetTypeInfo
, aby se vypsaly jejich typové informace, a poté jsou hodnoty porovnány pomocí funkce Equal
. To umožňuje rychleji získat typové informace a porovnávat typy.
Tato metoda může být také použita pro struktury, ukazatele a rozhraní a dokáže řešit často se vyskytující problém s "typed nil" (nil s typem). Hash
bude přiřazen, 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 práci s typem any
. Umožňuje flexibilní a rychlé zpracování hodnot různých typů a podrobné získání a využití informací o typech. Je však třeba poznamenat, že se jedná o skrytou funkci v rámci runtime a může být ovlivněna změnami ve specifikaci jazyka, proto je nutná opatrnost.