Why Go’s any and interface{} Are the Same… But Not Quite
Leapcell

Leapcell @leapcell

About: leapcell.io: serverless web hosting / async task / redis

Location:
California
Joined:
Jul 31, 2024

Why Go’s any and interface{} Are the Same… But Not Quite

Publish Date: Aug 3
0 0

Leapcell: The Best Serverless Web Hosting

any vs interface{} in Go: What's the Difference?

In a recent team discussion, someone claimed "any and interface{} in Go are identical." Technically, they're right—Go's official definition makes any an alias for interface{}:

type any = interface{}
Enter fullscreen mode Exit fullscreen mode

They're functionally equivalent under the hood. So why did the Go team introduce any? Semantics and readability:

  • interface{}: Represents "unconstrained dynamic types" (think JSON parsing, reflection). Emphasizes dynamism, requires type assertions.
  • any: Designed specifically for generics. Marks unconstrained type parameters, emphasizing type-safe generality—once a type is set, it stays consistent.

This distinction preserves backward compatibility while clarifying generics code.

The Problem Go Generics Solve

Before Go 1.18 (when generics arrived), developers repeated code for identical logic with different types. Example: summing number slices:

// Sum []int64
func SumInts(numbers []int64) int64 {
    var s int64
    for _, v := range numbers { s += v }
    return s
}

// Sum []float64 (same logic, different type)
func SumFloats(numbers []float64) float64 {
    var s float64
    for _, v := range numbers { s += v }
    return s
}
Enter fullscreen mode Exit fullscreen mode

This violates DRY (Don't Repeat Yourself) and increases maintenance costs.

Go Generics: Core Concepts

Go 1.18 introduced generics with 3 key ideas:

  • Type Parameters: Let functions/types use parameterized types.
  • Type Constraints: Define valid types for parameters via interfaces.
  • Type Inference: Automatically deduces types during calls, simplifying code.

Refactoring the Sum Function with Generics

// Generic function: works for int64 or float64
func SumNumbers[T int64 | float64](numbers []T) T {
    var s T
    for _, v := range numbers { s += v }
    return s
}
Enter fullscreen mode Exit fullscreen mode
  • [T int64 | float64] declares the type parameter: T is the type variable, int64 | float64 is the union constraint.
  • No need to specify types when calling (compiler infers them):
ints := []int64{1, 2, 3}
floats := []float64{1.1, 2.2, 3.3}

fmt.Println(SumNumbers(ints))   // 6
fmt.Println(SumNumbers(floats)) // 6.6
Enter fullscreen mode Exit fullscreen mode

Reusable Type Constraints

For complex/reusable constraints, define them as interfaces:

// Define a constraint for numbers
type Number interface {
    int64 | float64
}

// Refactored with custom constraint
func SumNumbers[T Number](numbers []T) T {
    var s T
    for _, v := range numbers { s += v }
    return s
}
Enter fullscreen mode Exit fullscreen mode

This boosts readability and maintainability.

How Go Generics Work: Performance Balance

Go's generics are efficient thanks to a unique approach: GC Shape Monomorphization + Dictionaries (not reflection or v-tables):

  1. GC Shape Monomorphization The compiler generates code based on a type's "GC shape" (size, alignment, pointer presence). For example:
    • int32, uint32, float32 share the same shape (4 bytes, no pointers) → reuse code.
    • All pointer types (*int, *string) share a shape → reuse code.

This avoids "code bloat" while matching native performance.

  1. Dictionary Technique For types with the same shape but different behavior (e.g., int vs float32 addition), the compiler uses hidden "dictionaries" to pass type-specific info (method addresses, operation functions).

Real-World Impact for Developers

  • Near-Native Performance: Arithmetic operations match non-generic code speed. Minimal overhead for dictionary-based method calls (on par with interface calls).
  • Controlled Binary Size: Code reuse for same "GC shape" types prevents bloat.

Conclusion

any and interface{} are technically equivalent, but their semantics signal Go's type system evolution: interface{} for dynamic typing, any for generics. Understanding this helps write clearer, more idiomatic Go.

Leapcell: The Best Serverless Web Hosting

Recommended platform for deploying Go services: Leapcell

🚀 Build with Your Favorite Language

Develop effortlessly in JavaScript, Python, Go, or Rust.

🌍 Deploy Unlimited Projects for Free

Only pay for what you use—no requests, no charges.

⚡ Pay-as-You-Go, No Hidden Costs

No idle fees, just seamless scalability.

📖 Explore Documentation

🔹 Follow on Twitter: @LeapcellHQ

Comments 0 total

    Add comment