---
id: 548504c5a0b4857763527603f3e1059e
author: snowmerak
title: Any er ikke Any, men Any er Any
description: Denne teksten analyserer i detalj Go-språkets `any`-type og den interne `EFace`-strukturen, og forklarer metoder for dynamisk typehåndtering ved bruk av `reflect`-pakken samt type-assertions og type-switcher.
language: "no"
date: 2025-09-30T15:12:53.689280321Z
path: /blog/posts/any-is-not-any-but-any-is-any-z6161856d
category: language
---

## Any

I Go-språket er any-typen et alias for `interface{}`, et magisk grensesnitt som kan inneholde alle typer. Det betyr at en variabel av typen any kan tilordnes en verdi av hvilken som helst type.

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

Slik sett gjør any-typen det mulig å lagre verdier av ulike typer, noe som gir rom for fleksibel kode. Ved bruk av any-typen må man imidlertid være oppmerksom på noen forhold.

### Type Assertion

For å bruke den faktiske verdien i en variabel av typen any må man bruke typeassertion (type assertion). Typeassertion gir kompilatoren beskjed om at en any-variabel har en bestemt type.

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

I eksempelet over fastslår `a.(string)` at `a` er av typen string. Dersom `a` ikke er av typen string, blir `ok` lik false, og man kan dermed unngå at programmet får panikk.

### Type Switch

Typebryter (type switch) gjør det mulig å utføre ulike handlinger avhengig av den faktiske typen til en any-variabel. Dette er nyttig når flere typer må håndteres.

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

I eksempelet over undersøker `a.(type)` den faktiske typen til `a` og utfører den casen som samsvarer med denne typen.

### Reflection

Ved hjelp av pakken reflect i Go kan man dynamisk undersøke og manipulere typen og verdien til en any-variabel under kjøretid. Dette er nyttig når man arbeider med komplekse datastrukturer.

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

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

I eksempelet over brukes pakken reflect til å kontrollere typen og verdien til `a` under kjøretid.

## EFace

I Go-språket deles grensesnitt inn i to hovedformer: EFace (Empty Interface) og IFace (Interface with Methods). Av disse er EFace det samme konseptet som any-typen vi har behandlet så langt.

### Struktur

EFace er en spesiell struktur som bare finnes inne i kjøretidsmiljøet (runtime) i Go, og derfor skal vi arbeide med en mimic (mimic) for å håndtere den her.

```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 har to felt:

- `Type`: En peker som inneholder metadata om den faktiske typen til verdien.
- `Data`: En peker som inneholder selve verdien.

I tillegg håndterer EFace typeinformasjon internt gjennom strukturen `runtimeType`. Denne strukturen inneholder ulike metadata, blant annet størrelse, justering og hashverdi for typen.

Det er særlig feltene `Kind_`, `Hash` og `Equal` vi bør merke oss. `Kind_` angir hvilken kategori typen tilhører, mens `Hash` og `Equal` brukes til å beregne hashverdier og sammenligne typer i hashtabeller.

### Innpakning

La oss nå pakke inn denne EFace-strukturen, slik at vi enklere kan utnytte den lavnivåmagien.

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

Funksjonen over tar imot en variabel av typen any, henter ut den interne EFace-strukturen og returnerer typeinformasjonen i form av en `TypeInfo`-struktur. Med `TypeInfo` kan man deretter enkelt få tilgang til typens hashverdi, kategori, sammenligningsfunksjon og så videre.

### Bruk

Bruken er svært enkel. Man sender ganske enkelt en variabel av typen any til funksjonen `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))
}
```

I eksempelet over sendes et heltall og et flyttall til `GetTypeInfo`, typeinformasjonen for hver av dem skrives ut, og funksjonen `Equal` brukes til å sammenligne verdiene. På denne måten kan man raskere hente ut typeinformasjon og sammenligne typer.

Metoden kan dessuten brukes på strukturer, pekere, grensesnitt og lignende, og den kan også løse det ikke ubetydelige problemet med typed nil (typed nil). `Hash` er tildelt så lenge typen finnes, mens `This` er 0 dersom verdien er nil, og dette gjør det mulig å skille tilfellene fra hverandre.

## Konklusjon

EFace spiller en viktig rolle i håndteringen av any-typen i Go-språket i dag. Det gir fleksibel og rask behandling av verdier av ulike typer, og gjør det mulig å hente ut og utnytte detaljert typeinformasjon. Dette er imidlertid en funksjonalitet som ligger skjult i kjøretidsmiljøet, og den kan påvirkes av endringer i språkspesifikasjonen, så man bør være forsiktig med bruken.
