---
id: 548504c5a0b4857763527603f3e1059e
author: snowmerak
title: Any не есть Any, но Any есть Any
description: Выполняется подробный анализ типа `any` языка Go и его внутренней структуры `EFace`, а также описываются способы динамической обработки типов с использованием пакета `reflect` и утверждений о типе и switch по типу.
language: ru
date: 2025-09-30T15:12:53.689280321Z
path: /blog/posts/any-is-not-any-but-any-is-any-z6161856d
category: language
---

## Any

В языке Go тип any является псевдонимом для `interface{}`, «волшебного» интерфейса, способного содержать значения любого типа. Другими словами, переменной типа any можно присвоить значение любого типа.

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

Таким образом, тип any позволяет хранить значения различных типов, что даёт возможность писать гибкий код. Однако при использовании типа any следует учитывать некоторые важные моменты.

### Type Assertion

Чтобы использовать фактическое значение из переменной типа any, необходимо применить утверждение типа (type assertion). Утверждение типа сообщает компилятору, что переменная any имеет определённый тип.

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

В приведённом примере выражение `a.(string)` утверждает, что `a` имеет тип string. Если `a` не является типом string, то `ok` принимает значение false, и это позволяет избежать паники программы.

### Type Switch

Конструкция переключения по типу (type switch) позволяет выполнять различные действия в зависимости от фактического типа переменной any. Это полезно, когда необходимо обрабатывать несколько типов.

```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")
}
```

В приведённом примере `a.(type)` проверяет фактический тип `a` и выполняет соответствующий ему case.

### Reflection

Пакет reflect в Go позволяет динамически проверять и изменять тип и значение переменной any во время выполнения программы. Это полезно при работе со сложными структурами данных.

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

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

В приведённом примере с помощью пакета reflect можно во время выполнения определить тип и значение `a`.

## EFace

В языке Go интерфейсы делятся на два основных вида: EFace (Empty Interface) и IFace (Interface with Methods). Из них EFace является тем же понятием, что и тип any, который мы рассматривали до сих пор.

### Структура

EFace является особой структурой, существующей только внутри среды выполнения Go, поэтому здесь мы будем работать с её мимикрией (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 содержит два поля:

- `Type`: указатель, который хранит метаданные о фактическом типе значения.
- `Data`: указатель, который хранит само значение.

Кроме того, EFace внутренне управляет информацией о типе через структуру `runtimeType`. Эта структура содержит различные метаданные о типе, включая размер, выравнивание, значение хеша и др.

Из этой структуры особого внимания заслуживают поля `Kind_`, `Hash` и `Equal`. `Kind_` обозначает вид типа, а `Hash` и `Equal` используются для вычисления хеш-значения и сравнения типов в хеш-таблицах.

### Оборачивание

Теперь обернём EFace, чтобы было удобнее использовать эту «чёрную магию».

```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,
	}
}
```

Функция принимает переменную типа any, извлекает из неё внутреннюю структуру EFace, получает из неё информацию о типе и возвращает её в виде структуры `TypeInfo`. Теперь с помощью `TypeInfo` можно легко получить доступ к хеш-значению типа, его виду, функции сравнения и другим данным.

### Использование

Использовать это очень просто: достаточно передать переменную типа any в функцию `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))
}
```

В приведённом примере целочисленное и вещественное значения передаются в функцию `GetTypeInfo`, выводится информация о каждом типе, а с помощью функции `Equal` значения сравниваются между собой. Таким образом, информацию о типе можно получить и сравнить быстрее.

Кроме того, этот подход можно так же применять к структурам, указателям, интерфейсам и т. д., а также решить распространённую проблему typed nil. Поле `Hash` будет заполнено, если тип задан, тогда как `This` равно нулю при nil, поэтому можно различить эти случаи.

## Заключение

EFace играет важную роль при работе с типом any в современном Go. С его помощью различные значения можно обрабатывать гибко и быстро, а также получать и использовать подробную информацию о типе. Однако это скрытая функциональность среды выполнения, которая может измениться вместе с изменениями спецификации языка, поэтому к её использованию следует относиться внимательно.
