Hone logo
Hone
Problems

Generic Data Structure with Type Constraints

This challenge focuses on leveraging Go's generics and type constraints to create a flexible yet safe data structure. You will build a generic stack that can hold elements of specific, predefined types, ensuring type safety at compile time. This is essential for building robust and reusable code that adheres to specific data requirements.

Problem Description

Your task is to implement a generic Stack data structure in Go. This stack should be able to hold elements of specific numeric types. You will define type constraints that allow the stack to accept only integers and floating-point numbers. The stack should support the standard operations: Push, Pop, and Peek.

Key Requirements:

  1. Generic Stack: The Stack should be a generic type that accepts any type T.
  2. Type Constraints: The type parameter T must be constrained to accept only numeric types that implement the constraints.Integer or constraints.Float interfaces from the golang.org/x/exp/constraints package.
  3. Push(item T): Adds an element to the top of the stack.
  4. Pop() (T, error): Removes and returns the top element from the stack. It should return an error if the stack is empty.
  5. Peek() (T, error): Returns the top element of the stack without removing it. It should return an error if the stack is empty.
  6. Empty Stack Handling: Implement appropriate error handling for Pop and Peek operations when the stack is empty.

Expected Behavior:

  • A stack created with int should only accept int values.
  • A stack created with float64 should only accept float64 values.
  • Attempting to push a non-numeric type (e.g., string, bool) into a constrained stack should result in a compile-time error.
  • Pop and Peek on an empty stack should return a zero value of the element type and a specific error (e.g., "stack is empty").

Edge Cases:

  • Pushing and popping multiple elements.
  • Popping from or peeking at an empty stack.
  • Creating stacks with different valid numeric types (e.g., int, int32, float32, float64).

Examples

Example 1:

package main

import (
	"fmt"
	"errors"
	"golang.org/x/exp/constraints"
)

// Define your Stack type and methods here
// ...

func main() {
	intStack := NewStack[int]()
	intStack.Push(10)
	intStack.Push(20)

	val, err := intStack.Pop()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Popped:", val) // Output: Popped: 20
	}

	val, err = intStack.Peek()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Peeked:", val) // Output: Peeked: 10
	}

	_, err = intStack.Pop()
	_, err = intStack.Pop()
	_, err = intStack.Pop() // Attempt to pop from empty stack
	if err != nil {
		fmt.Println("Error:", err) // Output: Error: stack is empty
	}
}

Example 2:

package main

import (
	"fmt"
	"errors"
	"golang.org/x/exp/constraints"
)

// Define your Stack type and methods here
// ...

func main() {
	floatStack := NewStack[float64]()
	floatStack.Push(3.14)
	floatStack.Push(1.618)

	val, err := floatStack.Peek()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Peeked:", val) // Output: Peeked: 1.618
	}

	val, err = floatStack.Pop()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Popped:", val) // Output: Popped: 1.618
	}
}

Example 3 (Illustrating Compile-Time Error):

package main

import (
	"fmt"
	"errors"
	"golang.org/x/exp/constraints"
)

// Define your Stack type and methods here
// ...

func main() {
	// This will cause a COMPILE-TIME ERROR because string is not a supported numeric type
	// stringStack := NewStack[string]()
	// stringStack.Push("hello")
}

Constraints

  • The stack should be able to hold a theoretically unlimited number of elements (no explicit capacity limit).
  • Input elements for Push will always be of type T where T satisfies the type constraints.
  • Your solution should not rely on reflection for type checking.
  • The use of the golang.org/x/exp/constraints package is permitted and encouraged.

Notes

  • Consider using a slice ([]T) to implement the underlying storage for your stack.
  • You will need to define a custom type for the error returned when the stack is empty.
  • Pay close attention to the syntax for defining generic types and type constraints in Go.
  • The golang.org/x/exp/constraints package provides useful interfaces for common numeric types. You can combine these using the union operator (|).
Loading editor...
go