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:
- Generic Stack: The
Stackshould be a generic type that accepts any typeT. - Type Constraints: The type parameter
Tmust be constrained to accept only numeric types that implement theconstraints.Integerorconstraints.Floatinterfaces from thegolang.org/x/exp/constraintspackage. Push(item T): Adds an element to the top of the stack.Pop() (T, error): Removes and returns the top element from the stack. It should return an error if the stack is empty.Peek() (T, error): Returns the top element of the stack without removing it. It should return an error if the stack is empty.- Empty Stack Handling: Implement appropriate error handling for
PopandPeekoperations when the stack is empty.
Expected Behavior:
- A stack created with
intshould only acceptintvalues. - A stack created with
float64should only acceptfloat64values. - Attempting to push a non-numeric type (e.g.,
string,bool) into a constrained stack should result in a compile-time error. PopandPeekon 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
Pushwill always be of typeTwhereTsatisfies the type constraints. - Your solution should not rely on reflection for type checking.
- The use of the
golang.org/x/exp/constraintspackage 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/constraintspackage provides useful interfaces for common numeric types. You can combine these using the union operator (|).