Lesson 15 of 18

Generic Types

Generic Structs

Just like functions, struct types can have type parameters. This lets you build reusable data structures that work with any type.

type Pair[T any, U any] struct {
    First  T
    Second U
}

You create instances by specifying the type arguments:

p := Pair[string, int]{First: "age", Second: 30}

Like Odo from Deep Space Nine shifting into whatever form the situation demands, a generic type adapts its shape to hold int, string, or anything you need.

Methods on Generic Types

Methods on a generic type must redeclare the type parameters in the receiver, but they cannot introduce new ones:

func (p Pair[T, U]) Swap() Pair[U, T] {
    return Pair[U, T]{First: p.Second, Second: p.First}
}

A Generic Container

Here is a practical example: a simple linked list:

type Node[T any] struct {
    Value T
    Next  *Node[T]
}

func (n *Node[T]) Append(val T) {
    current := n
    for current.Next != nil {
        current = current.Next
    }
    current.Next = &Node[T]{Value: val}
}

Type Constraint Interfaces

You can define interfaces that constrain type parameters to types supporting specific operations:

type Number interface {
    int | int8 | int16 | int32 | int64 | float32 | float64
}

type Stats[T Number] struct {
    Values []T
}

func (s Stats[T]) Sum() T {
    var total T
    for _, v := range s.Values {
        total += v
    }
    return total
}

Your Task

Implement a generic Stack[T any] struct backed by a slice. It should have three methods:

  • Push(val T) -- adds a value to the top of the stack
  • Pop() (T, bool) -- removes and returns the top value; returns the zero value of T and false if the stack is empty
  • Peek() (T, bool) -- returns the top value without removing it; returns the zero value of T and false if the stack is empty
Go runtime loading...
Loading...
Click "Run" to execute your code.