---
id: 548504c5a0b4857763527603f3e1059e
author: snowmerak
title: Any nie jest Any, ale Any jest Any
description: Szczegółowo analizujemy typ `any` języka Go oraz jego wewnętrzną strukturę `EFace`, a także wyjaśniamy metody dynamicznej obsługi typów z wykorzystaniem pakietu `reflect` oraz asercji i instrukcji switch dotyczących typów.
language: pl
date: 2025-09-30T15:12:53.689280321Z
path: /blog/posts/any-is-not-any-but-any-is-any-z6161856d
category: language
---

## Any

W języku Go typ any jest aliasem `interface{}`, magicznego interfejsu, który może przechowywać wartości dowolnego typu. Innymi słowy, do zmiennej typu any można przypisać wartość dowolnego typu.

```go
var a any
a = 42          // int
a = "hello"     // string
a = true        // bool
a = 3.14        // float64
```

Jak widać, typ any pozwala przechowywać wartości różnych typów, co umożliwia pisanie elastycznego kodu. Jednak podczas korzystania z typu any należy zwrócić uwagę na kilka kwestii.

### Type Assertion

Aby uzyskać rzeczywistą wartość ze zmiennej typu any, należy skorzystać z asercji typu (type assertion). Asercja typu informuje kompilator, że zmienna typu any jest określonego typu.

```go
var a any = "hello"
str, ok := a.(string)
if ok {
    fmt.Println("String value:", str)
} else {
    fmt.Println("Not a string")
}
```

W powyższym przykładzie `a.(string)` potwierdza, że `a` jest typu string. Jeśli `a` nie jest typu string, wartość `ok` wynosi false, co pozwala uniknąć wpadnięcia programu w panikę.

### Type Switch

Instrukcja przełącznika typu (type switch) pozwala wykonywać różne działania w zależności od rzeczywistego typu zmiennej typu any. Jest to przydatne, gdy należy obsłużyć wiele typów.

```go
var a any = 42
switch v := a.(type) {
case int:
    fmt.Println("Integer:", v)
case string:
    fmt.Println("String:", v)
case bool:
    fmt.Println("Boolean:", v)
default:
    fmt.Println("Unknown type")
}
```

W powyższym przykładzie `a.(type)` sprawdza rzeczywisty typ `a` i wykonuje przypadek odpowiadający temu typowi.

### Reflection

Korzystając z pakietu reflect języka Go, można dynamicznie sprawdzać i modyfikować typ oraz wartość zmiennej typu any w czasie działania programu. Jest to przydatne przy pracy ze złożonymi strukturami danych.

```go
import (
    "fmt"
    "reflect"
)

var a any = 3.14
v := reflect.ValueOf(a)
fmt.Println("Type:", v.Type())
fmt.Println("Value:", v.Float())
```

W powyższym przykładzie pakiet reflect służy do sprawdzenia typu i wartości `a` w czasie działania programu.

## EFace

W języku Go interfejsy dzielą się na dwie główne formy: EFace (Empty Interface) oraz IFace (Interface with Methods). Spośród nich EFace jest pojęciem tożsamym z typem any, o którym mówiliśmy dotychczas.

### Struktura

EFace jest specjalną strukturą istniejącą wyłącznie wewnątrz środowiska uruchomieniowego (runtime) Go, dlatego tutaj posłużymy się imitacją (mimic).

```go
type runtimeTypeMimic struct {
	Size_       uintptr
	PtrBytes    uintptr // number of (prefix) bytes in the type that can contain pointers
	Hash        uint32  // hash of type; avoids computation in hash tables
	TFlag       uint8   // extra type information flags
	Align_      uint8   // alignment of variable with this type
	FieldAlign_ uint8   // alignment of struct field with this type
	Kind_       uint8   // enumeration for C
	Equal       func(unsafe.Pointer, unsafe.Pointer) bool
	GCData      *byte
	Str         int32 // string form
	PtrToThis   int32 // type for pointer to this type, may be zero
}

type eFaceMimic struct {
	Type *runtimeTypeMimic
	Data unsafe.Pointer
}
```

EFace posiada dwa pola:

- `Type`: wskaźnik zawierający metadane o rzeczywistym typie wartości.
- `Data`: wskaźnik zawierający rzeczywistą wartość.

Ponadto EFace wewnętrznie zarządza informacjami o typie za pomocą struktury `runtimeType`. Struktura ta zawiera różne metadane, takie jak rozmiar typu, wyrównanie oraz wartość skrótu (hash).

Na co warto zwrócić szczególną uwagę w tej strukturze, to pola `Kind_`, `Hash` oraz `Equal`. `Kind_` określa rodzaj typu, natomiast `Hash` i `Equal` są wykorzystywane do obliczania wartości skrótu oraz porównywania typów w tablicach haszujących.

### Opakowanie

Teraz owińmy EFace w wrapper, aby ułatwić sobie korzystanie z czarnej magii.

```go
type TypeInfo struct {
	Hash  uint32
	TFlag uint8
	Kind  uint8
	Equal func(unsafe.Pointer, unsafe.Pointer) bool
	This  unsafe.Pointer
}

func GetTypeInfo(v any) TypeInfo {
	eface := *(*eFaceMimic)(unsafe.Pointer(&v))
	return TypeInfo{
		Hash:  eface.Type.Hash,
		TFlag: eface.Type.TFlag,
		Kind:  eface.Type.Kind_,
		Equal: eface.Type.Equal,
		This:  eface.Data,
	}
}
```

Powyższa funkcja przyjmuje zmienną typu any, wyodrębnia wewnętrzną strukturę EFace, pobiera z niej informacje o typie i zwraca je jako strukturę `TypeInfo`. Dzięki temu można łatwo uzyskać dostęp do wartości skrótu typu, jego rodzaju, funkcji porównującej itd.

### Zastosowanie

Użycie jest bardzo proste. Wystarczy przekazać zmienną typu any do funkcji `GetTypeInfo`.

```go
func main() {
	i := 42
	eface := GetTypeInfo(i)
	fmt.Printf("%+v\n", eface)

	f := 3.14
	eface2 := GetTypeInfo(f)
	fmt.Printf("%+v\n", eface2)

	// Compare the two values using the Equal function from the type info
	log.Println(eface.Equal(eface.This, eface.This))
	log.Println(eface2.Equal(eface2.This, eface2.This))
	log.Println(eface2.Equal(eface.This, eface2.This))
	log.Println(eface.Equal(eface2.This, eface.This))
}
```

W powyższym przykładzie wartości całkowitą i zmiennoprzecinkową przekazujemy do funkcji `GetTypeInfo`, aby wypisać informacje o ich typach, a następnie za pomocą funkcji `Equal` porównujemy wartości. Dzięki temu można szybciej uzyskać informacje o typie oraz porównywać typy.

Ta metoda może być również stosowana w przypadku struktur, wskaźników, interfejsów itp., a także pozwala rozwiązać częsty problem typed nil. Pole `Hash` jest przypisane, jeśli typ istnieje, natomiast `This` ma wartość 0 (nil), gdy wskaźnik jest pusty.

## Podsumowanie

EFace odgrywa istotną rolę w obsłudze typu any w języku Go obecnie. Pozwala to elastycznie i szybko przetwarzać wartości różnych typów oraz szczegółowo uzyskiwać i wykorzystywać informacje o typach. Należy jednak pamiętać, że jest to funkcjonalność ukryta w środowisku uruchomieniowym, a więc może ulec zmianie wraz ze zmianami specyfikacji języka.
