generics-go
path: courses/go-course/generics-go.md
- **fileName**: generics-go
- **Created on**: 2024-09-06 13:51:31
Understanding Generics in Go
Before generics, Go had to use interfaces and type assertions for generic behavior, which could be cumbersome and less type-safe. Generics make it possible to write code that can work with any data type while still retaining compile-time type safety.
Basic Example of Generics
Here’s a simple example of a generic function to illustrate how generics work in Go:
package main
import "fmt"
// Define a generic function
func Print[T any](value T) {
fmt.Println(value)
}
func main() {
Print(123) // Prints: 123
Print("Hello, Go!") // Prints: Hello, Go!
Print(45.67) // Prints: 45.67
}
Explanation:
Print[T any](value T) is a generic function where T is a type parameter.
any is a constraint that means T can be any type.
Print can be called with different types of arguments (e.g., int, string, float64), and it will work correctly for all of them.
Generics in Data Structures
Generics can also be used to create generic data structures. Here’s an example of a generic stack:
package main
import "fmt"
// Define a generic stack type
type Stack[T any] struct {
elements []T
}
// Push method for the Stack
func (s *Stack[T]) Push(element T) {
s.elements = append(s.elements, element)
}
// Pop method for the Stack
func (s *Stack[T]) Pop() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
element := s.elements[len(s.elements)-1]
s.elements = s.elements[:len(s.elements)-1]
return element, true
}
func main() {
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
fmt.Println(intStack.Pop()) // Prints: 2, true
fmt.Println(intStack.Pop()) // Prints: 1, true
fmt.Println(intStack.Pop()) // Prints: 0, false (default value for int)
stringStack := Stack[string]{}
stringStack.Push("foo")
stringStack.Push("bar")
fmt.Println(stringStack.Pop()) // Prints: bar, true
fmt.Println(stringStack.Pop()) // Prints: foo, true
}
Explanation:
Stack[T any] is a generic type with a type parameter T.
Push and Pop methods operate on the generic type T.
Push adds elements to the stack, and Pop removes and returns the top element of the stack.
The stack can hold values of any type (int, string, etc.).
Constraints
Go allows you to specify constraints on type parameters using interfaces. For example, if you want to ensure that a type parameter implements a specific interface, you can do so:
package main
import "fmt"
// Define an interface constraint
type Adder[T any] interface {
Add(a, b T) T
}
// Define a function that uses the Adder interface
func Sum[T any, A Adder[T]](a A, x, y T) T {
return a.Add(x, y)
}
// Implement Adder interface for int
type IntAdder struct{}
func (IntAdder) Add(a, b int) int {
return a + b
}
func main() {
intAdder := IntAdder{}
result := Sum(intAdder, 3, 4)
fmt.Println(result) // Prints: 7
}
Explanation:
Adder[T any] is an interface that has an Add method with type T.
Sum[T any, A Adder[T]] is a generic function that takes an Adder interface and two values of type T.
IntAdder implements the Adder interface for int type, providing the Add method.
Summary
Generics: Allow writing flexible and reusable code that can operate on any data type.
Type Parameters: Represent types in generic functions or data structures.
Constraints: Define specific type requirements for type parameters using interfaces.
continue:[[]]
before:[[]]