---
id: 548504c5a0b4857763527603f3e1059e
author: snowmerak
title: Any is niet Any, maar Any is Any
description: Dit document analyseert gedetailleerd het `any`-type van Go en de interne `EFace`-structuur, en beschrijft methoden voor dynamische typeverwerking met behulp van het `reflect`-pakket en type-assertions/type-switches.
language: nl
date: 2025-09-30T15:12:53.689280321Z
path: /blog/posts/any-is-not-any-but-any-is-any-z6161856d
category: language
---

## Any

In de Go-taal is het any-type een alias voor `interface{}`, een magische interface die waarden van elk type kan bevatten. Dat betekent dat elk type aan een any-variabele kan worden toegewezen.

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

Op deze manier stelt het any-type je in staat waarden van uiteenlopende typen op te slaan, wat flexibele code mogelijk maakt. Bij het gebruik van het any-type zijn er echter enkele aandachtspunten.

### Type Assertion

Om de daadwerkelijke waarde van een any-variabele te gebruiken, moet je een type assertion (type assertion) toepassen. Een type assertion vertelt de compiler dat een any-variabele een bepaald type heeft.

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

In het bovenstaande voorbeeld beweert `a.(string)` dat `a` van het type string is. Als `a` niet van het type string is, wordt `ok` false en kan worden voorkomen dat het programma in paniek raakt.

### Type Switch

Een type switch (type switch) stelt je in staat verschillende acties uit te voeren afhankelijk van het daadwerkelijke type van een any-variabele. Dit is nuttig wanneer je meerdere typen moet verwerken.

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

In het bovenstaande voorbeeld controleert `a.(type)` het daadwerkelijke type van `a` en voert het case uit dat bij dat type past.

### Reflection

Met het reflect-pakket van de Go-taal kun je het type en de waarde van een any-variabele tijdens runtime dynamisch inspecteren en manipuleren. Dit is nuttig bij het werken met complexe datastructuren.

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

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

In het bovenstaande voorbeeld gebruiken we het reflect-pakket om het type en de waarde van `a` tijdens runtime te controleren.

## EFace

In de Go-taal worden interfaces in twee hoofdvormen onderverdeeld: EFace (Empty Interface) en IFace (Interface with Methods). Hiervan is EFace hetzelfde concept als het any-type dat we tot nu toe hebben behandeld.

### Structuur

EFace is een speciale struct die alleen binnen de runtime van de Go-taal bestaat, daarom maken we hier een mimic (mimic) om ermee te werken.

```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 heeft twee velden:

- `Type`: een pointer die de metadata over het daadwerkelijke type van de waarde bevat.
- `Data`: een pointer die de daadwerkelijke waarde bevat.

Daarnaast beheert EFace de typeinformatie intern via de `runtimeType`-struct. Deze struct bevat diverse metadata, zoals de grootte van het type, de uitlijning en de hashwaarde.

Binnen deze struct zijn de velden `Kind_`, `Hash` en `Equal` de delen waar we op moeten letten. `Kind_` geeft het soort type aan, terwijl `Hash` en `Equal` worden gebruikt om types in hashtabellen te vergelijken en hashwaarden te berekenen.

### Verpakking

Laten we nu deze EFace inpakken, zodat we er op een handigere manier zwarte magie mee kunnen bedrijven.

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

Deze functie neemt een any-variabele, haalt de interne EFace-struct eruit, extraheert de typeinformatie daaruit en retourneert die als een `TypeInfo`-struct. Met deze `TypeInfo` kun je eenvoudig de hashwaarde, het soort en de vergelijkingsfunctie van het type benaderen.

### Gebruik

Het gebruik is heel eenvoudig. Je geeft gewoon een any-variabele door aan de functie `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))
}
```

In het bovenstaande voorbeeld geven we een geheel getal en een drijvendekommagetal door aan de functie `GetTypeInfo`, drukken we de typeinformatie van elk af en vergelijken we de waarden met de functie `Equal`. Zo kun je sneller typeinformatie verkrijgen en typen vergelijken.

Deze aanpak kan ook op structs, pointers, interfaces en dergelijke worden toegepast, en lost het veelvoorkomende probleem van het getypeerde nil (typed nil) op. `Hash` is toegewezen zolang er een type is, terwijl `This` nul is wanneer het nil is.

## Conclusie

EFace speelt een belangrijke rol bij het omgaan met het any-type in de Go-taal. Hiermee kunnen waarden van diverse typen flexibel en snel worden verwerkt, en kan gedetailleerde typeinformatie worden verkregen en gebruikt. Let er wel op dat dit een functionaliteit is die verborgen zit in de runtime en daardoor kan worden beïnvloed door wijzigingen in de taalspecificatie.
