Go

How do constraint interfaces in generics differ from ordinary interfaces?

Question 104HardGo 1.22 to 1.25

Since Go 1.18, an interface defines a type set. A basic interface (methods only) can be used both as a value type and as a constraint. An interface that contains type elements (int | string, ~float64) or embeds comparable is a constraint-only interface. You cannot declare a variable, field or parameter of that interface type.

type Number interface {
    ~int | ~int64 | ~float64 // ~ includes types whose underlying type is int, etc.
}

func Sum[T Number](xs []T) T {
    var s T
    for _, x := range xs {
        s += x // allowed: every type in the set supports +
    }
    return s
}

type Celsius float64
fmt.Println(Sum([]Celsius{1.5, 2.5})) // 4 (works thanks to ~float64)

// var n Number                // error: cannot use type Number outside a type constraint

// Mixing methods and type elements
type StringableInt interface {
    ~int
    String() string
}

// Type switch on a type parameter requires converting to any first
func Kind[T any](v T) string {
    switch any(v).(type) {
    case int:
        return "int"
    default:
        return "other"
    }
}

Key differences:

  • A call through a type parameter is resolved per GC shape. There is no boxing, but methods are still called indirectly through a dictionary.
  • Operators such as + and < are allowed only when every type in the set supports them.
  • comparable is satisfied by interface types since Go 1.20, so == can panic.
  • A value of type-parameter type cannot be type-asserted directly. Convert it to any first.

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions