Golang Generics Quiz

Golang
0 Passed
0% acceptance

35 comprehensive questions on Go's generics system introduced in Go 1.18, covering type parameters, generic functions, generic types, type constraints, and their benefits and limitations — with 15 code examples demonstrating practical generic programming patterns.

35 Questions
~70 minutes
1

Question 1

What are generics in Go?

go
func PrintSlice[T any](s []T) {
    for _, v := range s {
        fmt.Print(v, " ")
    }
    fmt.Println()
}

func main() {
    PrintSlice([]int{1, 2, 3})
    PrintSlice([]string{"a", "b", "c"})
}
A
Compile-time polymorphism allowing functions and types to work with multiple types using type parameters
B
Runtime type checking
C
Dynamic typing
D
No generics in Go
2

Question 2

How do you define a generic function with type parameters?

go
func Max[T comparable](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Max(10, 20))        // 20
    fmt.Println(Max("abc", "def")) // def
}
A
func FunctionName[T TypeConstraint](params) returnType { ... }
B
func FunctionName[T](params) returnType { ... }
C
func<T> FunctionName(params) returnType { ... }
D
Cannot define generic functions
3

Question 3

What is the 'any' constraint in Go generics?

go
func PrintValue[T any](v T) {
    fmt.Printf("Value: %v, Type: %T\n", v, v)
}

func main() {
    PrintValue(42)
    PrintValue("hello")
    PrintValue([]int{1, 2, 3})
}
A
Allows any type (equivalent to interface{})
B
Allows only basic types
C
Allows only comparable types
D
No such constraint exists
4

Question 4

How do you define a generic type (struct)?

go
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 {
    if len(s.items) == 0 {
        var zero T
        return zero
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item
}

func main() {
    intStack := Stack[int]{}
    intStack.Push(1)
    intStack.Push(2)
    fmt.Println(intStack.Pop()) // 2
}
A
type TypeName[T constraint] struct { ... }
B
type TypeName[T] struct { ... }
C
struct[T] TypeName { ... }
D
Cannot define generic types
5

Question 5

What is the 'comparable' constraint used for?

go
func Contains[T comparable](slice []T, item T) bool {
    for _, v := range slice {
        if v == item {
            return true
        }
    }
    return false
}

func IndexOf[T comparable](slice []T, item T) int {
    for i, v := range slice {
        if v == item {
            return i
        }
    }
    return -1
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(Contains(nums, 3))  // true
    fmt.Println(IndexOf(nums, 3))    // 2
}
A
Allows types that support == and != operators
B
Allows only numeric types
C
Allows only string types
D
No such constraint
6

Question 6

How do you create custom type constraints?

go
type Number interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64
}

func Sum[T Number](slice []T) T {
    var sum T
    for _, v := range slice {
        sum += v
    }
    return sum
}

func main() {
    ints := []int{1, 2, 3, 4, 5}
    floats := []float64{1.1, 2.2, 3.3}
    
    fmt.Println(Sum(ints))   // 15
    fmt.Println(Sum(floats)) // 6.6
}
A
Define interface types with union of underlying types using | and ~
B
Use struct types
C
Use function types
D
Cannot create custom constraints
7

Question 7

How do you implement a generic slice utility function?

go
func Filter[T any](slice []T, predicate func(T) bool) []T {
    var result []T
    for _, v := range slice {
        if predicate(v) {
            result = append(result, v)
        }
    }
    return result
}

func Map[T, U any](slice []T, transform func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = transform(v)
    }
    return result
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    
    // Filter even numbers
    evens := Filter(nums, func(n int) bool { return n%2 == 0 })
    fmt.Println(evens) // [2 4]
    
    // Map to strings
    strs := Map(nums, func(n int) string { return fmt.Sprintf("%d", n) })
    fmt.Println(strs) // [1 2 3 4 5]
}
A
Use type parameters for slice elements and function parameters
B
Use interface{}
C
Use reflection
D
Cannot implement generic slice utilities
8

Question 8

How do you handle multiple type parameters?

go
func Zip[T, U any](a []T, b []U) []Pair[T, U] {
    length := len(a)
    if len(b) < length {
        length = len(b)
    }
    
    result := make([]Pair[T, U], length)
    for i := 0; i < length; i++ {
        result[i] = Pair[T, U]{First: a[i], Second: b[i]}
    }
    return result
}

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

func main() {
    names := []string{"Alice", "Bob", "Charlie"}
    ages := []int{25, 30, 35}
    
    pairs := Zip(names, ages)
    for _, p := range pairs {
        fmt.Printf("%s is %d years old\n", p.First, p.Second)
    }
}
A
List multiple type parameters separated by commas: [T, U any]
B
Use nested brackets
C
Use separate brackets
D
Cannot use multiple type parameters
9

Question 9

How do you implement a generic Set type?

go
type Set[T comparable] map[T]bool

func NewSet[T comparable]() Set[T] {
    return make(Set[T])
}

func (s Set[T]) Add(item T) {
    s[item] = true
}

func (s Set[T]) Contains(item T) bool {
    return s[item]
}

func (s Set[T]) Remove(item T) {
    delete(s, item)
}

func (s Set[T]) Size() int {
    return len(s)
}

func main() {
    intSet := NewSet[int]()
    intSet.Add(1)
    intSet.Add(2)
    intSet.Add(1) // duplicate
    
    fmt.Println(intSet.Contains(1)) // true
    fmt.Println(intSet.Size())       // 2
}
A
Use map[T]bool with comparable constraint
B
Use []T slice
C
Use struct with slice
D
Cannot implement generic Set
10

Question 10

How do you use generics with interfaces?

go
type Container[T any] interface {
    Add(item T)
    Get(index int) T
    Size() int
}

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

func (sc *SliceContainer[T]) Add(item T) {
    sc.items = append(sc.items, item)
}

func (sc *SliceContainer[T]) Get(index int) T {
    if index < 0 || index >= len(sc.items) {
        var zero T
        return zero
    }
    return sc.items[index]
}

func (sc *SliceContainer[T]) Size() int {
    return len(sc.items)
}

func ProcessContainer[T any](c Container[T]) {
    c.Add(*new(T)) // This won't work as expected
    fmt.Println("Size:", c.Size())
}
A
Define generic interfaces and implement them with generic types
B
Use interface{}
C
Use reflection
D
Cannot use generics with interfaces
11

Question 11

How do you implement a generic sorting function?

go
func Sort[T any](slice []T, less func(a, b T) bool) {
    for i := 0; i < len(slice); i++ {
        for j := i + 1; j < len(slice); j++ {
            if less(slice[j], slice[i]) {
                slice[i], slice[j] = slice[j], slice[i]
            }
        }
    }
}

func main() {
    // Sort integers
    nums := []int{3, 1, 4, 1, 5}
    Sort(nums, func(a, b int) bool { return a < b })
    fmt.Println(nums) // [1 1 3 4 5]
    
    // Sort strings by length
    words := []string{"a", "bbb", "cc"}
    Sort(words, func(a, b string) bool { return len(a) < len(b) })
    fmt.Println(words) // [a cc bbb]
}
A
Pass comparison function as parameter to generic sort function
B
Use constraints with ordered types
C
Use built-in sort package
D
Cannot implement generic sorting
12

Question 12

How do you handle type inference with generics?

go
func Max[T comparable](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func MakePair[T, U any](first T, second U) Pair[T, U] {
    return Pair[T, U]{First: first, Second: second}
}

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

func main() {
    // Type inferred from arguments
    result1 := Max(10, 20)        // int
    result2 := Max("abc", "def") // string
    
    // Type inferred from assignment
    var p Pair[int, string] = MakePair(42, "hello")
    
    // Type parameters can be inferred
    p2 := MakePair("world", 3.14) // Pair[string, float64]
    
    fmt.Println(result1, result2, p, p2)
}
A
Go infers type parameters from function arguments and context
B
Must always specify type parameters explicitly
C
Use type assertions
D
No type inference with generics
13

Question 13

How do you implement a generic cache?

go
type Cache[K comparable, V any] struct {
    data map[K]V
}

func NewCache[K comparable, V any]() *Cache[K, V] {
    return &Cache[K, V]{
        data: make(map[K]V),
    }
}

func (c *Cache[K, V]) Set(key K, value V) {
    c.data[key] = value
}

func (c *Cache[K, V]) Get(key K) (V, bool) {
    value, exists := c.data[key]
    return value, exists
}

func (c *Cache[K, V]) Delete(key K) {
    delete(c.data, key)
}

func main() {
    // String to int cache
    stringCache := NewCache[string, int]()
    stringCache.Set("answer", 42)
    
    if val, ok := stringCache.Get("answer"); ok {
        fmt.Println("Cached value:", val)
    }
    
    // Int to string cache
    intCache := NewCache[int, string]()
    intCache.Set(1, "one")
    
    if val, ok := intCache.Get(1); ok {
        fmt.Println("Cached value:", val)
    }
}
A
Use map[K]V with comparable key constraint
B
Use interface{} for keys and values
C
Use reflection
D
Cannot implement generic cache
14

Question 14

How do you implement method constraints?

go
type Stringer interface {
    String() string
}

func PrintAll[T Stringer](items []T) {
    for _, item := range items {
        fmt.Println(item.String())
    }
}

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}

func main() {
    people := []Person{
        {Name: "Alice", Age: 30},
        {Name: "Bob", Age: 25},
    }
    
    PrintAll(people)
}
A
Define interface with required methods as constraint
B
Use struct constraints
C
Use function constraints
D
Cannot constrain methods
15

Question 15

How do you implement a generic linked list?

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

type LinkedList[T any] struct {
    Head *Node[T]
    Size int
}

func (ll *LinkedList[T]) Append(value T) {
    newNode := &Node[T]{Value: value}
    
    if ll.Head == nil {
        ll.Head = newNode
    } else {
        current := ll.Head
        for current.Next != nil {
            current = current.Next
        }
        current.Next = newNode
    }
    ll.Size++
}

func (ll *LinkedList[T]) ToSlice() []T {
    result := make([]T, 0, ll.Size)
    current := ll.Head
    for current != nil {
        result = append(result, current.Value)
        current = current.Next
    }
    return result
}

func main() {
    list := &LinkedList[string]{}
    list.Append("Hello")
    list.Append("World")
    
    fmt.Println(list.ToSlice()) // [Hello World]
}
A
Define generic Node and LinkedList types with recursive references
B
Use interface{}
C
Use slices
D
Cannot implement generic linked list
16

Question 16

What are the benefits of using generics over interface{}?

A
Compile-time type safety, better performance, clearer code, IDE support
B
Runtime flexibility
C
Dynamic typing
D
No benefits
17

Question 17

How do you implement a generic binary tree?

go
type TreeNode[T any] struct {
    Value T
    Left  *TreeNode[T]
    Right *TreeNode[T]
}

func Insert[T any](root *TreeNode[T], value T, less func(a, b T) bool) *TreeNode[T] {
    if root == nil {
        return &TreeNode[T]{Value: value}
    }
    
    if less(value, root.Value) {
        root.Left = Insert(root.Left, value, less)
    } else {
        root.Right = Insert(root.Right, value, less)
    }
    
    return root
}

func InOrder[T any](root *TreeNode[T], visit func(T)) {
    if root == nil {
        return
    }
    InOrder(root.Left, visit)
    visit(root.Value)
    InOrder(root.Right, visit)
}

func main() {
    var root *TreeNode[int]
    root = Insert(root, 5, func(a, b int) bool { return a < b })
    root = Insert(root, 3, func(a, b int) bool { return a < b })
    root = Insert(root, 7, func(a, b int) bool { return a < b })
    
    InOrder(root, func(v int) { fmt.Print(v, " ") }) // 3 5 7
}
A
Use recursive generic types with comparison function parameter
B
Use interface{}
C
Use built-in tree types
D
Cannot implement generic binary tree
18

Question 18

How do you handle generics with channels?

go
func Producer[T any](ch chan<- T, values []T) {
    for _, v := range values {
        ch <- v
    }
    close(ch)
}

func Consumer[T any](ch <-chan T, process func(T)) {
    for v := range ch {
        process(v)
    }
}

func Pipeline[T, U any](input <-chan T, transform func(T) U) <-chan U {
    output := make(chan U)
    go func() {
        defer close(output)
        for v := range input {
            output <- transform(v)
        }
    }()
    return output
}

func main() {
    input := make(chan int, 5)
    go Producer(input, []int{1, 2, 3, 4, 5})
    
    doubled := Pipeline(input, func(x int) int { return x * 2 })
    
    Consumer(doubled, func(v int) {
        fmt.Println(v)
    })
}
A
Use generic channel types in producer/consumer patterns
B
Use interface{} channels
C
Use reflection
D
Cannot use generics with channels
19

Question 19

How do you implement a generic priority queue?

go
type PriorityQueue[T any] struct {
    items []T
    less  func(a, b T) bool
}

func NewPriorityQueue[T any](less func(a, b T) bool) *PriorityQueue[T] {
    return &PriorityQueue[T]{
        items: make([]T, 0),
        less:  less,
    }
}

func (pq *PriorityQueue[T]) Push(item T) {
    pq.items = append(pq.items, item)
    // Simple bubble up (not efficient)
    for i := len(pq.items) - 1; i > 0; i-- {
        if pq.less(pq.items[i], pq.items[i-1]) {
            pq.items[i], pq.items[i-1] = pq.items[i-1], pq.items[i]
        } else {
            break
        }
    }
}

func (pq *PriorityQueue[T]) Pop() T {
    if len(pq.items) == 0 {
        var zero T
        return zero
    }
    item := pq.items[0]
    pq.items = pq.items[1:]
    return item
}

func main() {
    pq := NewPriorityQueue(func(a, b int) bool { return a < b }) // min-heap
    pq.Push(3)
    pq.Push(1)
    pq.Push(4)
    
    fmt.Println(pq.Pop()) // 1
    fmt.Println(pq.Pop()) // 3
}
A
Use slice with custom comparison function
B
Use built-in heap package
C
Use interface{}
D
Cannot implement generic priority queue
20

Question 20

What are the limitations of Go generics?

A
No specialization, no operator overloading, limited type inference, no variadic type parameters
B
No performance benefits
C
Cannot use with interfaces
D
No limitations
21

Question 21

How do you implement a generic graph data structure?

go
type Graph[T comparable] struct {
    vertices map[T][]T
}

func NewGraph[T comparable]() *Graph[T] {
    return &Graph[T]{
        vertices: make(map[T][]T),
    }
}

func (g *Graph[T]) AddVertex(vertex T) {
    if _, exists := g.vertices[vertex]; !exists {
        g.vertices[vertex] = []T{}
    }
}

func (g *Graph[T]) AddEdge(from, to T) {
    g.AddVertex(from)
    g.AddVertex(to)
    g.vertices[from] = append(g.vertices[from], to)
}

func (g *Graph[T]) GetNeighbors(vertex T) []T {
    return g.vertices[vertex]
}

func main() {
    g := NewGraph[string]()
    g.AddEdge("A", "B")
    g.AddEdge("A", "C")
    g.AddEdge("B", "C")
    
    fmt.Println("A neighbors:", g.GetNeighbors("A")) // [B C]
}
A
Use map[T][]T with comparable vertex constraint
B
Use interface{}
C
Use adjacency matrix
D
Cannot implement generic graph
22

Question 22

How do you implement a generic result type (like Rust's Result)?

go
type Result[T any] struct {
    value T
    err   error
}

func Ok[T any](value T) Result[T] {
    return Result[T]{value: value}
}

func Err[T any](err error) Result[T] {
    return Result[T]{err: err}
}

func (r Result[T]) IsOk() bool {
    return r.err == nil
}

func (r Result[T]) IsErr() bool {
    return r.err != nil
}

func (r Result[T]) Unwrap() T {
    if r.err != nil {
        panic(r.err)
    }
    return r.value
}

func (r Result[T]) UnwrapOr(defaultValue T) T {
    if r.err != nil {
        return defaultValue
    }
    return r.value
}

func Divide(a, b float64) Result[float64] {
    if b == 0 {
        return Err[float64](errors.New("division by zero"))
    }
    return Ok(a / b)
}

func main() {
    result1 := Divide(10, 2)
    if result1.IsOk() {
        fmt.Println("Result:", result1.Unwrap())
    }
    
    result2 := Divide(10, 0)
    if result2.IsErr() {
        fmt.Println("Error:", result2.err)
    }
}
A
Use generic struct with value and error fields
B
Use panic/recover
C
Use multiple return values
D
Cannot implement Result type
23

Question 23

How do you implement a generic singleton pattern?

go
type Singleton[T any] struct {
    instance *T
    once     sync.Once
}

func (s *Singleton[T]) GetInstance() *T {
    s.once.Do(func() {
        s.instance = new(T)
    })
    return s.instance
}

// Global instances
var (
    intSingleton    = &Singleton[int]{}
    stringSingleton = &Singleton[string]{}
)

func main() {
    // Get int singleton
    intInstance1 := intSingleton.GetInstance()
    intInstance2 := intSingleton.GetInstance()
    
    fmt.Println("Same instance:", intInstance1 == intInstance2) // true
    
    *intInstance1 = 42
    fmt.Println("Value:", *intInstance2) // 42
}
A
Use sync.Once with generic type
B
Use global variables
C
Use mutex
D
Cannot implement generic singleton
24

Question 24

How do you implement a generic observer pattern?

go
type Observer[T any] interface {
    OnNotify(data T)
}

type Subject[T any] struct {
    observers []Observer[T]
}

func (s *Subject[T]) Subscribe(observer Observer[T]) {
    s.observers = append(s.observers, observer)
}

func (s *Subject[T]) Notify(data T) {
    for _, observer := range s.observers {
        observer.OnNotify(data)
    }
}

type Printer struct{}

func (p *Printer) OnNotify(data int) {
    fmt.Println("Received:", data)
}

func main() {
    subject := &Subject[int]{}
    printer := &Printer{}
    
    subject.Subscribe(printer)
    subject.Notify(42) // Received: 42
}
A
Use generic interfaces for observers and subjects
B
Use channels
C
Use callbacks
D
Cannot implement generic observer pattern
25

Question 25

How do you implement a generic factory pattern?

go
type Factory[T any] func() T

func Register[T any](name string, factory Factory[T]) {
    // In real implementation, use a map[string]interface{}
    // and type assertions or reflection
}

func Create[T any](name string) T {
    // Lookup factory and call it
    var zero T
    return zero // placeholder
}

// Better implementation
type FactoryMap struct {
    factories map[string]interface{}
}

func (fm *FactoryMap) Register(name string, factory interface{}) {
    if fm.factories == nil {
        fm.factories = make(map[string]interface{})
    }
    fm.factories[name] = factory
}

func (fm *FactoryMap) Create(name string) interface{} {
    if factory, exists := fm.factories[name]; exists {
        // Use reflection to call factory
        // This is simplified
        return factory
    }
    return nil
}

func main() {
    fm := &FactoryMap{}
    
    // Register factories
    fm.Register("int", func() int { return 42 })
    fm.Register("string", func() string { return "hello" })
    
    // Create instances
    intFactory := fm.factories["int"].(func() int)
    fmt.Println(intFactory()) // 42
}
A
Use function types as factories with type parameters
B
Use reflection
C
Use interface{}
D
Cannot implement generic factory
26

Question 26

How do you implement a generic decorator pattern?

go
type Handler[T, U any] func(T) U

func LoggingDecorator[T, U any](handler Handler[T, U]) Handler[T, U] {
    return func(input T) U {
        start := time.Now()
        fmt.Printf("Calling handler with input: %v\n", input)
        
        result := handler(input)
        
        duration := time.Since(start)
        fmt.Printf("Handler returned: %v (took %v)\n", result, duration)
        
        return result
    }
}

func CachingDecorator[T comparable, U any](handler Handler[T, U]) Handler[T, U] {
    cache := make(map[T]U)
    return func(input T) U {
        if result, exists := cache[input]; exists {
            fmt.Println("Cache hit for:", input)
            return result
        }
        
        result := handler(input)
        cache[input] = result
        return result
    }
}

func processNumber(n int) string {
    time.Sleep(100 * time.Millisecond) // simulate work
    return fmt.Sprintf("processed_%d", n)
}

func main() {
    handler := LoggingDecorator(CachingDecorator(processNumber))
    
    fmt.Println(handler(1))
    fmt.Println(handler(1)) // cache hit
}
A
Use generic function types and return wrapped functions
B
Use interfaces
C
Use inheritance
D
Cannot implement generic decorator
27

Question 27

How do you implement a generic visitor pattern?

go
type Visitor[T any] interface {
    Visit(T)
}

type Element[T any] interface {
    Accept(Visitor[T])
}

type ConcreteElement[T any] struct {
    Value T
}

func (ce *ConcreteElement[T]) Accept(visitor Visitor[T]) {
    visitor.Visit(ce.Value)
}

func (ce *ConcreteElement[T]) GetValue() T {
    return ce.Value
}

type PrintingVisitor[T any] struct{}

func (pv *PrintingVisitor[T]) Visit(value T) {
    fmt.Printf("Visiting: %v\n", value)
}

func main() {
    element := &ConcreteElement[int]{Value: 42}
    visitor := &PrintingVisitor[int]{}
    
    element.Accept(visitor) // Visiting: 42
}
A
Use generic interfaces for visitors and elements
B
Use reflection
C
Use type switches
D
Cannot implement generic visitor pattern
28

Question 28

How do you implement a generic state machine?

go
type State[T any] func(T) State[T]

type StateMachine[T any] struct {
    currentState State[T]
    context      T
}

func NewStateMachine[T any](initialState State[T], context T) *StateMachine[T] {
    return &StateMachine[T]{
        currentState: initialState,
        context:      context,
    }
}

func (sm *StateMachine[T]) Process() {
    for sm.currentState != nil {
        sm.currentState = sm.currentState(sm.context)
    }
}

func (sm *StateMachine[T]) SendEvent(event T) {
    if sm.currentState != nil {
        sm.currentState = sm.currentState(event)
    }
}

func main() {
    // Simple counter state machine
    count := 0
    
    incrementState := func(event int) State[int] {
        count += event
        fmt.Println("Count:", count)
        return incrementState
    }
    
    sm := NewStateMachine(incrementState, 0)
    sm.SendEvent(1)
    sm.SendEvent(2)
    sm.SendEvent(3)
}
A
Use function types as states with generic context
B
Use interfaces
C
Use structs
D
Cannot implement generic state machine
29

Question 29

How do you implement a generic event system?

go
type EventHandler[T any] func(T)

type EventBus[T any] struct {
    handlers map[string][]EventHandler[T]
}

func NewEventBus[T any]() *EventBus[T] {
    return &EventBus[T]{
        handlers: make(map[string][]EventHandler[T]),
    }
}

func (eb *EventBus[T]) Subscribe(eventType string, handler EventHandler[T]) {
    eb.handlers[eventType] = append(eb.handlers[eventType], handler)
}

func (eb *EventBus[T]) Publish(eventType string, data T) {
    if handlers, exists := eb.handlers[eventType]; exists {
        for _, handler := range handlers {
            handler(data)
        }
    }
}

func main() {
    bus := NewEventBus[string]()
    
    bus.Subscribe("message", func(msg string) {
        fmt.Println("Received:", msg)
    })
    
    bus.Publish("message", "Hello World")
}
A
Use map of event types to generic handler slices
B
Use channels
C
Use interfaces
D
Cannot implement generic event system
30

Question 30

How do you implement a generic configuration system?

go
type Config[T any] struct {
    data map[string]T
}

func NewConfig[T any]() *Config[T] {
    return &Config[T]{
        data: make(map[string]T),
    }
}

func (c *Config[T]) Set(key string, value T) {
    c.data[key] = value
}

func (c *Config[T]) Get(key string) (T, bool) {
    value, exists := c.data[key]
    return value, exists
}

func (c *Config[T]) GetOrDefault(key string, defaultValue T) T {
    if value, exists := c.Get(key); exists {
        return value
    }
    return defaultValue
}

// Typed configurations
type AppConfig struct {
    Port     Config[int]
    Host     Config[string]
    Timeout  Config[time.Duration]
}

func main() {
    config := &AppConfig{
        Port:     *NewConfig[int](),
        Host:     *NewConfig[string](),
        Timeout:  *NewConfig[time.Duration](),
    }
    
    config.Port.Set("server_port", 8080)
    config.Host.Set("server_host", "localhost")
    
    port := config.Port.GetOrDefault("server_port", 3000)
    host := config.Host.GetOrDefault("server_host", "127.0.0.1")
    
    fmt.Printf("Server: %s:%d\n", host, port)
}
A
Use generic config types for type-safe configuration
B
Use interface{}
C
Use JSON unmarshaling
D
Cannot implement generic configuration
31

Question 31

How do you implement a generic validation system?

go
type Validator[T any] func(T) error

type ValidationRule[T any] struct {
    Name      string
    Validator Validator[T]
}

type ValidatorSet[T any] struct {
    rules []ValidationRule[T]
}

func NewValidatorSet[T any]() *ValidatorSet[T] {
    return &ValidatorSet[T]{}
}

func (vs *ValidatorSet[T]) AddRule(name string, validator Validator[T]) {
    vs.rules = append(vs.rules, ValidationRule[T]{
        Name:      name,
        Validator: validator,
    })
}

func (vs *ValidatorSet[T]) Validate(value T) []error {
    var errors []error
    for _, rule := range vs.rules {
        if err := rule.Validator(value); err != nil {
            errors = append(errors, fmt.Errorf("%s: %w", rule.Name, err))
        }
    }
    return errors
}

func main() {
    stringValidator := NewValidatorSet[string]()
    stringValidator.AddRule("not_empty", func(s string) error {
        if s == "" {
            return errors.New("string cannot be empty")
        }
        return nil
    })
    stringValidator.AddRule("min_length", func(s string) error {
        if len(s) < 3 {
            return errors.New("string must be at least 3 characters")
        }
        return nil
    })
    
    errors := stringValidator.Validate("hi")
    for _, err := range errors {
        fmt.Println("Validation error:", err)
    }
}
A
Use generic validator functions and rule sets
B
Use reflection
C
Use type assertions
D
Cannot implement generic validation
32

Question 32

How do you implement a generic serialization system?

go
type Serializer[T any] interface {
    Serialize(T) ([]byte, error)
    Deserialize([]byte) (T, error)
}

type JSONSerializer[T any] struct{}

func (js *JSONSerializer[T]) Serialize(value T) ([]byte, error) {
    return json.Marshal(value)
}

func (js *JSONSerializer[T]) Deserialize(data []byte) (T, error) {
    var value T
    err := json.Unmarshal(data, &value)
    return value, err
}

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    serializer := &JSONSerializer[Person]{}
    
    person := Person{Name: "Alice", Age: 30}
    
    data, err := serializer.Serialize(person)
    if err != nil {
        fmt.Println("Serialize error:", err)
        return
    }
    
    fmt.Println("JSON:", string(data))
    
    restored, err := serializer.Deserialize(data)
    if err != nil {
        fmt.Println("Deserialize error:", err)
        return
    }
    
    fmt.Printf("Restored: %+v\n", restored)
}
A
Use generic serializer interfaces with concrete implementations
B
Use reflection
C
Use interface{}
D
Cannot implement generic serialization
33

Question 33

How do you implement a generic dependency injection container?

go
type Factory[T any] func() T

type Container struct {
    factories map[string]interface{}
}

func NewContainer() *Container {
    return &Container{
        factories: make(map[string]interface{}),
    }
}

func (c *Container) Register[T any](name string, factory Factory[T]) {
    c.factories[name] = factory
}

func (c *Container) Resolve[T any](name string) T {
    if factory, exists := c.factories[name]; exists {
        if typedFactory, ok := factory.(Factory[T]); ok {
            return typedFactory()
        }
    }
    var zero T
    return zero
}

func main() {
    container := NewContainer()
    
    // Register services
    container.Register("logger", func() *Logger { return &Logger{} })
    container.Register("database", func() *Database { return &Database{} })
    
    // Resolve services
    logger := container.Resolve[*Logger]("logger")
    db := container.Resolve[*Database]("database")
    
    fmt.Printf("Logger: %p, DB: %p\n", logger, db)
}

// Placeholder types
type Logger struct{}
type Database struct{}
A
Use map of factories with type assertions for resolution
B
Use reflection
C
Use global variables
D
Cannot implement generic DI container
34

Question 34

How do you implement a generic middleware system?

go
type Middleware[T any] func(T) T

type Chain[T any] struct {
    middlewares []Middleware[T]
}

func NewChain[T any]() *Chain[T] {
    return &Chain[T]{}
}

func (c *Chain[T]) Use(middleware Middleware[T]) {
    c.middlewares = append(c.middlewares, middleware)
}

func (c *Chain[T]) Execute(input T) T {
    result := input
    for _, middleware := range c.middlewares {
        result = middleware(result)
    }
    return result
}

func main() {
    chain := NewChain[string]()
    
    // Add logging middleware
    chain.Use(func(s string) string {
        fmt.Println("Processing:", s)
        return s
    })
    
    // Add uppercase middleware
    chain.Use(func(s string) string {
        return strings.ToUpper(s)
    })
    
    result := chain.Execute("hello world")
    fmt.Println("Final result:", result)
}
A
Use slice of generic middleware functions
B
Use interfaces
C
Use channels
D
Cannot implement generic middleware
35

Question 35

What is the most important consideration when using Go generics?

A
Use generics when you need compile-time type safety and code reuse, but prefer interfaces for polymorphism
B
Always use generics
C
Never use generics
D
Use generics only for performance

QUIZZES IN Golang