<PageIntro>
Das Empty Interface ist das Interface ohne Methoden — und weil jeder Typ in Go automatisch null Methoden erfüllt, ist es der Universalcontainer der Sprache. Vor Go 1.18 hieß es ausschließlich interface{}; seit der Generics-Einführung ist any ein gleichwertiger Alias und der idiomatisch bevorzugte Name. Dieser Artikel ordnet, wo any legitim und nützlich ist — JSON ohne Schema, heterogene Container, variadische Print-Funktionen — und wo es ein Code-Smell geworden ist, seit Generics existieren. Dazu kommen die unsichtbaren Kosten: jedes any = primitive ist eine Heap-Allokation, jede Type-Assertion ein Laufzeit-Check, jede falsche Annahme ein Panic-Risiko.
</PageIntro>
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>01 · Abschnitt</div>
Was ist das Empty Interface?
Die Go-Spec definiert Interface-Typen als Method-Sets — eine Menge von Methoden-Signaturen, die ein Typ erfüllen muss, um das Interface zu implementieren:
> An interface type defines a type set. A variable of interface type can store a value of any type that is in the type set of the interface.
Wenn das Method-Set leer ist, gibt es nichts zu erfüllen — und damit erfüllt es jeder Typ der Sprache. int, string, struct{}, func(), chan bool, *os.File, ein anonymer Struct mit zweihundert Feldern: alle passen.
<CodeBlock lang="Go" filename="empty-interface.go" showOutput=true> <div slot="code">
package main
import "fmt"
func main() {
var x interface{}
x = 42
fmt.Printf("%T: %v\n", x, x)
x = "hallo"
fmt.Printf("%T: %v\n", x, x)
x = []int{1, 2, 3}
fmt.Printf("%T: %v\n", x, x)
x = struct{ Name string }{"Alice"}
fmt.Printf("%T: %v\n", x, x)
}
</div> <div slot="output">
int: 42
string: hallo
[]int: [1 2 3]
struct { Name string }: {Alice}
</div> </CodeBlock>
Eine interface{}-Variable speichert intern zwei Wörter: einen Type-Descriptor (welcher konkrete Typ liegt drin) und einen Daten-Pointer (auf den Wert selbst). Diese Zwei-Wort-Repräsentation ist der Grund, warum any so flexibel — und so teuer — ist.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>02 · Abschnitt</div>
any ab Go 1.18 — derselbe Typ, besserer Name
Mit dem Release 1.18 (März 2022) erschien Generics — und im selben Atemzug ein neuer Alias im builtin-Paket:
> any is an alias for interface{} and is equivalent to interface{} in all ways.
Das ist wörtlich zu nehmen. any ist kein neuer Typ, keine Subkategorie, kein Wrapper. Der Compiler ersetzt any durch interface{} an jeder Verwendungsstelle. Die folgenden zwei Deklarationen sind identisch:
<CodeBlock lang="Go" filename="any-alias.go"> <div slot="code">
var a interface{}
var b any
// a und b haben exakt denselben Typ — Zuweisung in beide
// Richtungen funktioniert ohne Konvertierung.
a = b
b = a
</div> </CodeBlock>
Die Go-Doku-Konvention seit 1.18: Neuer Code schreibt any. Bestehender Code mit interface{} ist nicht falsch, aber die Stdlib wurde in 1.18 großflächig umgestellt (fmt.Println(a ...any), json.Unmarshal(data []byte, v any), sync.Map.Load-Signaturen). Wer heute interface{} in neuem Code sieht, liest entweder vor-1.18-Quellen oder hat ein Linting-Versäumnis erwischt.
<CodeBlock lang="Go" filename="any-stdlib.go"> <div slot="code">
// Vor Go 1.18:
// func Println(a ...interface{}) (n int, err error)
// Seit Go 1.18 (aktuelle Signatur):
// func Println(a ...any) (n int, err error)
// Identisches Verhalten, bessere Lesbarkeit.
</div> </CodeBlock>
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>03 · Abschnitt</div>
Wann ist any legitim?
Drei Einsatzgebiete sind heute, trotz Generics, noch genuin sinnvoll:
(1) JSON ohne festes Schema. Wenn du JSON parst, dessen Struktur du nicht statisch kennst — Konfigurations-Dateien mit beliebigen Schlüsseln, dynamische API-Antworten, Webhook-Payloads von Drittparteien — ist map[string]any das idiomatische Ziel:
<CodeBlock lang="Go" filename="json-schemalos.go" showOutput=true> <div slot="code">
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := []byte(`{
"name": "Alice",
"age": 30,
"active": true,
"scores": [95, 87, 92]
}`)
var result map[string]any
if err := json.Unmarshal(data, &result); err != nil {
panic(err)
}
for k, v := range result {
fmt.Printf("%s: %v (%T)\n", k, v, v)
}
}
</div> <div slot="output">
name: Alice (string)
age: 30 (float64)
active: true (bool)
scores: [95 87 92] ([]interface {})
</div> </CodeBlock>
Beachte: json.Unmarshal mappt alle JSON-Zahlen auf float64 und alle JSON-Arrays auf []any. Wer Integer braucht, muss konvertieren — ein klassischer Stolperstein bei Schema-loser Verarbeitung.
(2) Variadische Print- und Format-Funktionen. fmt.Println, fmt.Printf, log.Printf — sie nehmen ...any, weil sie jeden Wert formatieren können sollen. Generics helfen hier nicht: die Funktion will heterogene Argumente (int, string, error gemischt), nicht einen einheitlichen Type-Parameter.
<CodeBlock lang="Go" filename="variadic-any.go" showOutput=true> <div slot="code">
package main
import "fmt"
// Ein eigener Logger, der gemischte Werte annimmt — `any` ist
// hier die richtige Wahl, kein Generic-Type-Parameter.
func logKV(pairs ...any) {
for i := 0; i < len(pairs); i += 2 {
fmt.Printf("%v=%v ", pairs[i], pairs[i+1])
}
fmt.Println()
}
func main() {
logKV("user", "alice", "age", 30, "active", true)
}
</div> <div slot="output">
user=alice age=30 active=true
</div> </CodeBlock>
(3) Heterogene Container. Wenn eine Liste oder Map bewusst Werte unterschiedlicher Typen aufnimmt — ein Event-Log mit verschiedenen Event-Strukturen, ein Cache mit gemischten Payloads, ein Plugin-Registry — dann ist []any oder map[string]any ehrlich. Generics würden den Container auf einen Typ festnageln, was hier nicht gewünscht ist.
<PageSplitter />
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>04 · Abschnitt</div>
Wann ist any ein Code-Smell?
Vor Go 1.18 war interface{} der einzige Weg, eine generische Datenstruktur oder Funktion zu schreiben. Heute ist Generics da — und damit verlieren viele alte any-Stellen ihre Daseinsberechtigung.
<CodeBlock lang="Go" filename="any-anti-pattern.go"> <div slot="code">
// ANTI-PATTERN — `any` für eine Container-Klasse, deren Inhalt
// in Wahrheit immer derselbe Typ ist:
type Stack struct {
items []any
}
func (s *Stack) Push(x any) { s.items = append(s.items, x) }
func (s *Stack) Pop() any { /* ... */ return nil }
// Aufrufer muss bei jedem Pop assertieren:
// v := s.Pop().(int)
// — und kriegt zur Laufzeit einen Panic, wenn der Typ daneben liegt.
</div> </CodeBlock>
<CodeBlock lang="Go" filename="generic-statt-any.go"> <div slot="code">
// RICHTIG seit 1.18 — Generic-Stack mit Type-Parameter:
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(x T) { s.items = append(s.items, x) }
func (s *Stack[T]) Pop() T {
n := len(s.items) - 1
v := s.items[n]
s.items = s.items[:n]
return v
}
// Aufrufer:
// s := Stack[int]{}
// s.Push(42)
// v := s.Pop() // v ist statisch int — kein Cast, kein Panic-Risiko.
</div> </CodeBlock>
Die Faustregel: Wenn jeder Aufrufer am Ende eine Type-Assertion macht, war any die falsche Wahl. Type-Assertion ist Laufzeit-Information, die zur Compile-Zeit verfügbar gewesen wäre — Generics machen sie unnötig.
Die zweite Front: API-Doku. Eine Funktion func Process(x any) any sagt dem Leser nichts. Welche Typen darf ich reinstecken? Was kommt zurück? Die Antwort steckt im Kommentar — wenn überhaupt — und wird vom Compiler nicht geprüft. func Process[T Numeric](x T) T dagegen ist selbstdokumentierend.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>05 · Abschnitt</div>
Boxing — die unsichtbaren Heap-Allokationen
Wenn du einen primitiven Wert in ein any steckst, passiert Boxing: der Wert wird auf den Heap kopiert, und das Interface speichert einen Pointer auf diese Heap-Allokation. Das ist nicht gratis — und in Hot-Paths messbar.
<CodeBlock lang="Go" filename="boxing-allokation.go" showOutput=true> <div slot="code">
package main
import (
"fmt"
"runtime"
)
func boxInt() any {
return 42 // int → any: erzwingt Heap-Allokation
}
func main() {
var m1, m2 runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&m1)
const N = 1_000_000
sink := make([]any, 0, N)
for i := 0; i < N; i++ {
sink = append(sink, boxInt())
}
runtime.ReadMemStats(&m2)
fmt.Printf("Heap-Allokationen: %d\n", m2.Mallocs-m1.Mallocs)
_ = sink
}
</div> <div slot="output">
Heap-Allokationen: 1000003
</div> </CodeBlock>
Eine Million int-Werte in ein []any zu packen erzeugt eine Million Heap-Allokationen — plus den Garbage-Collector-Druck danach. Dieselbe Million int in einem []int liegt in einem zusammenhängenden Backing-Array, ohne einzelne Allokationen.
Der Compiler hat in jüngeren Go-Versionen zwar Optimierungen, die kleine Integer (0–255) und feste bool-Werte vorab im Static-Storage halten — aber das ist Implementierungs-Detail, kein Garantie-Versprechen. Wer Performance braucht, vermeidet any in heißen Schleifen.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>06 · Abschnitt</div>
Type-Assertion — wieder heraus aus dem any
Was rein ging, muss wieder raus. Der Mechanismus heißt Type-Assertion und hat zwei Formen:
<CodeBlock lang="Go" filename="type-assertion.go" showOutput=true> <div slot="code">
package main
import "fmt"
func main() {
var x any = 42
// Single-Value-Form: paniciert bei falschem Typ.
n := x.(int)
fmt.Println("n =", n)
// Comma-Ok-Form: liefert (Wert, false) statt Panic.
if s, ok := x.(string); ok {
fmt.Println("string:", s)
} else {
fmt.Println("kein string, ok =", ok)
}
// Defensiv für unbekannte Eingaben — immer comma-ok benutzen.
inputs := []any{1, "zwei", 3.14, true}
for _, in := range inputs {
if i, ok := in.(int); ok {
fmt.Printf("int: %d\n", i)
} else {
fmt.Printf("nicht-int: %v\n", in)
}
}
}
</div> <div slot="output">
n = 42
kein string, ok = false
int: 1
nicht-int: zwei
nicht-int: 3.14
nicht-int: true
</div> </CodeBlock>
Single-Value-Form (x.(int)) ist die kompromisslose Variante: stimmt der Typ nicht, paniciert das Programm mit interface conversion: interface {} is string, not int. Nur benutzen, wenn du sicher bist — etwa direkt nach einem Type-Switch, der den Typ bereits geprüft hat.
Comma-Ok-Form (v, ok := x.(int)) ist der defensive Default. ok ist true bei erfolgreichem Typ-Match, sonst false — und v ist im Fehlerfall der Zero-Value des Zieltyps, kein Panic. Für JSON-Verarbeitung, externe Eingaben, alles unbekannte: immer comma-ok.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>07 · Abschnitt</div>
Type-Switch — strukturierte Verzweigung über any
Wenn du mehrere mögliche Typen prüfen musst, ist ein Type-Switch lesbarer als eine Kette aus if-comma-ok:
<CodeBlock lang="Go" filename="type-switch.go" showOutput=true> <div slot="code">
package main
import "fmt"
func describe(x any) string {
switch v := x.(type) {
case nil:
return "nil"
case int:
return fmt.Sprintf("int %d", v)
case string:
return fmt.Sprintf("string %q (len=%d)", v, len(v))
case bool:
return fmt.Sprintf("bool %t", v)
case []any:
return fmt.Sprintf("Slice mit %d Elementen", len(v))
case map[string]any:
return fmt.Sprintf("Map mit %d Schlüsseln", len(v))
default:
return fmt.Sprintf("unbekannter Typ %T", v)
}
}
func main() {
inputs := []any{
nil,
42,
"hallo",
true,
[]any{1, 2, 3},
map[string]any{"a": 1},
3.14,
}
for _, in := range inputs {
fmt.Println(describe(in))
}
}
</div> <div slot="output">
nil
int 42
string "hallo" (len=5)
bool true
Slice mit 3 Elementen
Map mit 2 Schlüsseln
unbekannter Typ float64
</div> </CodeBlock>
Innerhalb jedes case-Zweigs ist v bereits auf den passenden Typ konvertiert — kein zusätzlicher Cast nötig. Der default-Zweig sieht den ursprünglichen any-Wert und ist die richtige Stelle, um unerwartete Typen als Fehler zu behandeln.
<PageSplitter />
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>08 · Abschnitt</div>
Praxis-Beispiel — JSON-Konfigurations-Parser
Ein realistisches Szenario: deine Anwendung liest eine Konfiguration im JSON-Format, deren Struktur teilweise dynamisch ist. Plugins können beliebige Felder einbringen, der Kern erwartet aber ein paar bekannte Schlüssel. map[string]any ist hier nicht Code-Smell, sondern korrekt — und der saubere Umgang sieht so aus:
<CodeBlock lang="Go" filename="config-parser.go" showOutput=true> <div slot="code">
package main
import (
"encoding/json"
"fmt"
)
type Config struct {
Host string
Port int
Plugins map[string]any // Plugin-Subkonfigurationen, beliebige Schemas
}
func parseConfig(raw []byte) (*Config, error) {
var m map[string]any
if err := json.Unmarshal(raw, &m); err != nil {
return nil, fmt.Errorf("ungültiges JSON: %w", err)
}
cfg := &Config{Plugins: map[string]any{}}
// Pflichtfeld: host — string erforderlich.
host, ok := m["host"].(string)
if !ok {
return nil, fmt.Errorf("host fehlt oder ist kein string")
}
cfg.Host = host
// Pflichtfeld: port — JSON-Number landet als float64.
portF, ok := m["port"].(float64)
if !ok {
return nil, fmt.Errorf("port fehlt oder ist keine Zahl")
}
cfg.Port = int(portF)
// Optional: plugins — sub-map mit beliebigen Schlüsseln.
if p, ok := m["plugins"].(map[string]any); ok {
cfg.Plugins = p
}
return cfg, nil
}
func main() {
raw := []byte(`{
"host": "0.0.0.0",
"port": 8080,
"plugins": {
"auth": {"provider": "oauth2", "issuer": "https://example.com"},
"metrics": {"enabled": true, "interval_s": 30}
}
}`)
cfg, err := parseConfig(raw)
if err != nil {
panic(err)
}
fmt.Printf("Host: %s\nPort: %d\nPlugins: %d konfiguriert\n",
cfg.Host, cfg.Port, len(cfg.Plugins))
for name, pcfg := range cfg.Plugins {
fmt.Printf(" %s: %v\n", name, pcfg)
}
}
</div> <div slot="output">
Host: 0.0.0.0
Port: 8080
Plugins: 2 konfiguriert
auth: map[issuer:https://example.com provider:oauth2]
metrics: map[enabled:true interval_s:30]
</div> </CodeBlock>
Drei Beobachtungen:
- Comma-Ok überall. Jeder Zugriff auf die Map nutzt die Comma-Ok-Form der Type-Assertion. Kein einziger Aufruf könnte paniciern, egal wie kaputt das eingehende JSON ist.
float64für Integer. Der Port kommt alsfloat64aus dem Unmarshal — wer Integer-Semantik will, muss explizit casten. Wenn die Eingabe8080.5lautet, geht der Cast schief und du brauchst zusätzliche Validierung.anybleibt im Plugin-Bereich. Der Kern (Host, Port) ist getypt; nur das, was strukturell offen sein muss, bleibtany. Das ist die saubere Trennung.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>09 · Abschnitt</div>
Praxis-Beispiel — Result-Wrapper, dann Refactor zu Generic
Ein häufiges Muster vor Generics war der Result-Typ mit any-Payload. Heute schaut man genauer hin und ersetzt ihn durch ein Generic — ein realistischer Refactor-Pfad:
<CodeBlock lang="Go" filename="result-vor-generic.go"> <div slot="code">
// VOR Go 1.18 — Result mit any-Payload.
type Result struct {
Value any
Err error
}
func fetchUser(id int) Result {
// Implementation gibt einen *User zurück, in Result gepackt.
return Result{Value: &User{ID: id, Name: "Alice"}}
}
// Aufrufer muss bei jedem Zugriff assertieren:
// r := fetchUser(1)
// if r.Err != nil { ... }
// u := r.Value.(*User) // Panic-Risiko, kein Compile-Check
</div> </CodeBlock>
<CodeBlock lang="Go" filename="result-nach-generic.go" showOutput=true> <div slot="code">
package main
import "fmt"
// SEIT Go 1.18 — Result generisch über Payload-Typ.
type Result[T any] struct {
Value T
Err error
}
func Ok[T any](v T) Result[T] { return Result[T]{Value: v} }
func Fail[T any](e error) Result[T] { return Result[T]{Err: e} }
type User struct {
ID int
Name string
}
func fetchUser(id int) Result[*User] {
if id <= 0 {
return Fail[*User](fmt.Errorf("invalid id %d", id))
}
return Ok(&User{ID: id, Name: "Alice"})
}
func main() {
r := fetchUser(1)
if r.Err != nil {
fmt.Println("Fehler:", r.Err)
return
}
// r.Value ist statisch *User — kein Cast, kein Panic-Risiko.
fmt.Printf("User: %+v\n", r.Value)
}
</div> <div slot="output">
User: &{ID:1 Name:Alice}
</div> </CodeBlock>
Der Unterschied ist mehr als kosmetisch: das Generic-Result[*User] garantiert zur Compile-Zeit, dass der Aufrufer einen *User aus dem Value-Feld bekommt. Die any-Variante verschiebt diese Garantie auf die Laufzeit, mit Panic-Risiko, wenn jemand falsch assertiert.
Faustregel für das Refactoring: Suche im Code nach .(-Type-Assertions. Jede, die direkt nach einem Funktions-Rückgabewert oder einem Container-Zugriff steht, ist ein Kandidat für Generics. Wenn der Aufrufer den Typ schon kennt — warum versteckt man ihn dann hinter any?
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>10 · Abschnitt</div>
Generics ersetzen any — wo es wirklich passt
Eine kurze Karte: was vor 1.18 mit any gemacht wurde, und was heute besser mit Generics geht.
<TableContainer>
| Pre-1.18 Muster | Heute idiomatisch |
|---|---|
func Min(a, b any) any | func Min[T cmp.Ordered](a, b T) T |
type Set struct { m map[any]struct{} } | type Set[T comparable] struct { m map[T]struct{} } |
func Map(xs []any, f func(any) any) []any | func Map[A, B any](xs []A, f func(A) B) []B |
func Contains(xs []any, x any) bool | func Contains[T comparable](xs []T, x T) bool |
interface{}-Pool / -Cache | Pool[T any] mit konkretem Type-Parameter |
| Linked-List, Stack, Queue, Tree | Alle generisch über Element-Typ |
</TableContainer>
any als Type-Parameter (func F[T any](x T)) ist nicht dasselbe wie any als Parameter-Typ (func F(x any)). Das any im Type-Parameter sagt „beliebiger Typ, aber statisch festgelegt pro Aufruf" — beim Parameter-Typ heißt es „beliebiger Typ, dynamisch bei jedem Argument". Erster Fall behält Typsicherheit, zweiter Fall gibt sie auf.
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>11 · Abschnitt</div>
Reflection — der nächste Schritt nach any
Type-Assertion und Type-Switch reichen, solange du die möglichen Typen kennst. Für komplett unbekannte Eingaben — etwa ein generischer Pretty-Printer, ein Validator, ein ORM — brauchst du Reflection aus reflect. Reflection nimmt any als Eintrittspunkt und legt Type-Descriptor und Daten-Pointer offen:
<CodeBlock lang="Go" filename="reflect-einstieg.go" showOutput=true> <div slot="code">
package main
import (
"fmt"
"reflect"
)
func inspect(x any) {
v := reflect.ValueOf(x)
t := v.Type()
fmt.Printf("Typ: %s, Kind: %s, Wert: %v\n", t, v.Kind(), v)
}
func main() {
inspect(42)
inspect("hallo")
inspect([]int{1, 2, 3})
inspect(struct{ Name string }{"Alice"})
}
</div> <div slot="output">
Typ: int, Kind: int, Wert: 42
Typ: string, Kind: string, Wert: hallo
Typ: []int, Kind: slice, Wert: [1 2 3]
Typ: struct { Name string }, Kind: struct, Wert: {Alice}
</div> </CodeBlock>
Reflection ist mächtig, aber teuer (deutlich teurer als Type-Assertion) und schwer zu warten — der Compiler prüft nichts mehr. Daumen-Regel: Reflection ist die letzte Stufe, nicht die erste. Wer Generics oder Type-Switch nutzen kann, sollte das tun.
<PageSplitter />
<div class="sectionTag"><span class="bar" aria-hidden="true"></span>12 · Abschnitt</div>
<Insights kind="pitfalls">
<InsightItem title="JSON-Zahlen landen immer als float64 in any.">
json.Unmarshal mappt JSON-Numbers auf float64, nicht auf int. Wer aus einem map[string]any einen Integer holt, schreibt n := int(m["x"].(float64)) — die direkte Assertion m["x"].(int) paniciert. Wer Integer-Semantik braucht, nutzt json.Number oder unmarshaled in einen getypten Struct.
</InsightItem>
<InsightItem title="Boxing eines primitiven Werts ist eine Heap-Allokation.">
var x any = 42 allokiert auf dem Heap. In einer engen Schleife mit Millionen Zuweisungen ist das messbar — GC-Druck steigt, Cache-Lokalität sinkt. Für Hot-Paths: konkrete Typen oder Generics statt any.
</InsightItem>
<InsightItem title="x.(T) ohne comma-ok paniciert bei falschem Typ.">
Die Single-Value-Form der Type-Assertion ist unverzeihlich — falscher Typ heißt panic: interface conversion. Nur einsetzen, wenn der Typ garantiert korrekt ist (z. B. direkt nach einem Type-Switch). Für alle Eingaben aus JSON, externen Quellen, generischen Containern: immer v, ok := x.(T).
</InsightItem>
<InsightItem title="any als Type-Parameter ist nicht dasselbe wie any als Parameter.">
func F[T any](x T) ist Generic — T wird pro Aufruf statisch festgelegt, Typsicherheit bleibt. func F(x any) ist Empty-Interface — x darf bei jedem Aufruf einen anderen Typ haben, Typprüfung erst zur Laufzeit. Verwechslung führt zu API-Designs, die wie Generics aussehen, aber keine sind.
</InsightItem>
<InsightItem title="interface{} und any sind identisch — aber neuer Code nimmt any.">
Der Compiler behandelt beide gleich, Mischverwendung in einer Datei ist nicht falsch, aber unsauber. Linting-Tools wie revive oder staticcheck können auf interface{} in neuem Code hinweisen — die Stdlib selbst hat in 1.18 vollständig auf any umgestellt.
</InsightItem>
<InsightItem title="[]any ist nicht zuweisungs-kompatibel zu []int.">
Selbst wenn jedes Element eines []any ein int ist, kannst du den Slice nicht direkt einer []int-Variable zuweisen. Du musst elementweise kopieren und assertieren. Das ist eine bewusste Sprach-Entscheidung — der Speicher-Layout von []any (Pointer auf gebox-te Werte) unterscheidet sich vom []int (zusammenhängende Integer im Array).
</InsightItem>
<InsightItem title="any-Container haben schlechte Cache-Lokalität.">
[]int{1, 2, 3, ...} ist ein zusammenhängendes Array. []any{1, 2, 3, ...} ist ein Array aus Interface-Headern, die jeweils auf eine separate Heap-Allokation zeigen. Iteration über letzteres springt durch den Speicher — bei großen Datenmengen ein Performance-Killer.
</InsightItem>
<InsightItem title="Wer func Process(x any) any schreibt, hat fast immer Generics gemeint.">
Eine Funktion, die jeden Wert annimmt und einen verwandten Typ zurückgibt, sagt mit any nichts über die Beziehung zwischen Eingabe und Ausgabe. func Process[T any](x T) T macht explizit, dass derselbe Typ rein- und rausgeht — und der Compiler erzwingt es. any in diesen Signaturen ist seit Go 1.18 ein Refactoring-Kandidat.
</InsightItem>
</Insights>
<h2 id="resources">Weiterführende Ressourcen</h2>
<h3 id="external-sources">Externe Quellen</h3>
- Interface types — Go Language Specification
- Type assertions — Go Language Specification
- Type switches — Go Language Specification
anyimbuiltin-Paket- Go 1.18 Release Notes —
anyals Alias encoding/json— Unmarshal inany- Effective Go: Interfaces and other types
<h3 id="related">Verwandte Artikel</h3>
- Interfaces — Übersicht und Method-Sets
- Implizite Implementierung — Strukturelles Interface-Matching
- Type-Assertion und Type-Switch — der Weg aus
anyheraus - Interface Satisfaction — wann ein Typ ein Interface erfüllt
- nil-Interface-Fallen — typisiertes nil vs. echtes nil
- Accept Interfaces, Return Structs — das Postel'sche API-Idiom