---
title: "The comparable Constraint in Go Generics"
date: 2024-12-25
tags: ["go"]
description: "What 'incomparable types in type set' means and how Go's comparable constraint shapes generic maps and sets."
---

While working on a generic function in Go, I once encountered this error: *'incomparable types in type set'*.

It led me to dig deeper into the `comparable` constraint. With this post, I'll walk you through what `comparable` is, why it's useful, and how you can leverage it for cleaner, type-safe Go code.

## Why comparable?

`comparable` is a constraint that ensures a type supports equality comparisons (`==` and `!=`).

In Go, only certain types are inherently comparable: primitive types like `int`, `float64` and `string`, but exclude types like slices, maps, and functions.

```go
type Array[T comparable] struct {
    data []T
}

func (a *Array[T]) Contains(value T) bool {
    for _, v := range a.data {
        if v == value {
            return true
        }
    }
    return false
}
```

### Constraint Hierarchy

```mermaid
graph TD
    A["any\n(all types)"]
    B["comparable\n(supports == and !=)"]
    C["constraints.Ordered\n(supports < > <= >=)"]
    A --> B
    B --> C
```

### any vs. interface{}

`any` is simply an alias for `interface{}`. While functionally identical, `any` better aligns with the intentions of generics and improves readability.

```go
func PrintValues[T any](values []T) {
    for _, v := range values {
        fmt.Println(v)
    }
}
```

### Real-World Scenarios

#### 1. Deduplication with comparable

```go
func RemoveDuplicates[T comparable](input []T) []T {
    seen := make(map[T]bool)
    result := []T{}
    for _, v := range input {
        if !seen[v] {
            seen[v] = true
            result = append(result, v)
        }
    }
    return result
}
```

### Compile-Time vs Runtime

```mermaid
graph LR
    subgraph "Using any (unsafe)"
        A1["func Contains\n[T any]"] --> B1["Pass []int\nas T"]
        B1 --> C1["Runtime Panic\n== on slice"]
    end
    subgraph "Using comparable (safe)"
        A2["func Contains\n[T comparable]"] --> B2["Pass []int\nas T"]
        B2 --> C2["Compile Error\n[]int not comparable"]
    end
```

The real value of `comparable` is catching errors at compile time instead of letting them slip into production.
