C#
GO

C# to Go

10 lessons

Progress0%
1Introduction2Type System3Classes to Structs4Interfaces5Error Handling6Async to Goroutines7Generics8LINQ to Slices9Testing10Packages and Modules
All Mirror Courses
C#
GO
Generics
MirrorLesson 7 of 10
Lesson 7

Generics

Generics

Introduction

In this lesson, you'll learn about generics in Go. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C#
From C#:

In C#, you're familiar with generics.

GO
In Go:

Go has its own approach to generics, which we'll explore step by step.

The Go Way

Let's see how Go handles this concept. Here's a typical example:

GO
Go Example
package main

import "cmp"

// Go generics (Go 1.18+) — type constraints via interfaces
func Max[T cmp.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() T {
    n := len(s.items) - 1
    item := s.items[n]
    s.items = s.items[:n]
    return item
}

Comparing to C#

Here's how you might have written similar code in C#:

C#
C# (What you know)
// C# generics — rich constraints
T Max<T>(T a, T b) where T : IComparable<T> {
    return a.CompareTo(b) > 0 ? a : b;
}

class Stack<T> {
    private List<T> _items = new();
    public void Push(T item) => _items.Add(item);
    public T Pop() => _items[^1]; // simplified
}
Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go generics use [T constraint] syntax — square brackets, not angle brackets

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Constraints are interfaces — cmp.Ordered, any, comparable are built-in

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go lacks some C# constraints: no new(), no struct/class distinction

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

C# generics are more mature with more constraint types

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go's any = interface{}, comparable = types usable as map keys

Step-by-Step Breakdown

1. Generic Function Syntax

Go uses [TypeParam Constraint] before the parameter list. The constraint is an interface that defines what operations are available.

C#
C#
T Identity<T>(T value) => value;
T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) > 0 ? a : b;
GO
Go
func Identity[T any](value T) T {
    return value
}

// cmp.Ordered = any type supporting < > <= >=
func Max[T cmp.Ordered](a, b T) T {
    if a > b { return a }
    return b
}

2. Generic Types

Generic structs use the same [T constraint] syntax. When calling methods on a generic type, Go infers the type argument.

C#
C#
class Pair<T, U> {
    public T First { get; set; }
    public U Second { get; set; }
}
var p = new Pair<string, int> { First = "x", Second = 1 };
GO
Go
type Pair[T, U any] struct {
    First  T
    Second U
}

p := Pair[string, int]{First: "x", Second: 1}

// Type inference in function calls
func MakePair[T, U any](f T, s U) Pair[T, U] {
    return Pair[T, U]{First: f, Second: s}
}
p2 := MakePair("x", 1)  // inferred

3. Custom Constraints

Go constraints are interfaces. Use the ~ operator for underlying-type matching — useful for type aliases.

C#
C#
// C# — multiple constraints
void Process<T>(T item)
    where T : IEntity, IComparable<T>, new() { }
GO
Go
// Go — constraint is just an interface
type Number interface {
    ~int | ~int64 | ~float64  // union of types
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

4. Generics vs Interface{}

Before Go 1.18, developers used interface{} for generic-like code with runtime type assertions. Generics provide compile-time safety instead.

C#
C#
// Old Go (pre-1.18) — like C# object, loses type info
func OldMax(a, b interface{}) interface{} {
    // type assertion needed — not type safe
}
GO
Go
// Modern Go (1.18+) — type safe generics
func Max[T cmp.Ordered](a, b T) T {
    if a > b { return a }
    return b
}

// No type assertions needed:
n := Max(3, 5)        // int
f := Max(3.1, 2.9)    // float64
Rule of Thumb
Use generics when you find yourself writing the same logic for multiple types. Don't use them for simple cases where an interface works — Go prefers simplicity.

Common Mistakes

When coming from C#, developers often make these mistakes:

  • Go generics use [T constraint] syntax — square brackets, not angle brackets
  • Constraints are interfaces — cmp.Ordered, any, comparable are built-in
  • Go lacks some C# constraints: no new(), no struct/class distinction
Common Pitfall
Don't assume Go works exactly like C#. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Go generics use [T Constraint] square brackets, not angle brackets
  • Constraints are interfaces: any, comparable, cmp.Ordered, or custom
  • ~ in constraints matches underlying types (for type aliases)
  • Generics replaced the unsafe interface{}/any pattern from pre-1.18 code
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C# code in Go to practice these concepts.
PreviousNext