<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, &amp;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 &lt; 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(&amp;m1)

    const N = 1_000_000
    sink := make([]any, 0, N)
    for i := 0; i &lt; N; i++ {
        sink = append(sink, boxInt())
    }

    runtime.ReadMemStats(&amp;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, &amp;m); err != nil {
        return nil, fmt.Errorf("ungültiges JSON: %w", err)
    }

    cfg := &amp;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.
  • float64 für Integer. Der Port kommt als float64 aus dem Unmarshal — wer Integer-Semantik will, muss explizit casten. Wenn die Eingabe 8080.5 lautet, geht der Cast schief und du brauchst zusätzliche Validierung.
  • any bleibt im Plugin-Bereich. Der Kern (Host, Port) ist getypt; nur das, was strukturell offen sein muss, bleibt any. 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: &amp;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 &lt;= 0 {
        return Fail[*User](fmt.Errorf("invalid id %d", id))
    }
    return Ok(&amp;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: &amp;{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 MusterHeute idiomatisch
func Min(a, b any) anyfunc 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) []anyfunc Map[A, B any](xs []A, f func(A) B) []B
func Contains(xs []any, x any) boolfunc Contains[T comparable](xs []T, x T) bool
interface{}-Pool / -CachePool[T any] mit konkretem Type-Parameter
Linked-List, Stack, Queue, TreeAlle 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>

<h3 id="related">Verwandte Artikel</h3>

/ Weiter

Zurück zu Interfaces

Zur Übersicht