Skip to content

Instantly share code, notes, and snippets.

@unijad

unijad/main.go Secret

Created July 30, 2023 10:36
Show Gist options
  • Save unijad/d8cede6305e8cea1316402c2ee8321a0 to your computer and use it in GitHub Desktop.
Save unijad/d8cede6305e8cea1316402c2ee8321a0 to your computer and use it in GitHub Desktop.
Go 1.21 Generic Functions Examples
package main
import (
"bytes"
"encoding/gob"
"fmt"
"reflect"
"strings"
)
func main() {
basicUsage()
SumGenericsUsage()
SerializeUsage()
DeserializeUsage()
ValidateUsage()
ValidateUsage2()
ValidateAddressUsage()
ValidateUserUsage()
}
func First[T any](items []T) T {
return items[0]
}
func basicUsage() {
intSlice := []int{1, 2, 3, 4, 5}
firstInt := First[int](intSlice) // returns 1
println(firstInt)
stringSlice := []string{"apple", "banana", "cherry"}
firstString := First[string](stringSlice) // returns "apple"
println(firstString)
}
// optional types in generic functions
func SumGenerics[T int | int16 | int32 | int64 | int8 | float32 | float64](a, b T) T {
return a + b
}
func SumGenericsUsage() {
myInt1 := int32(1)
myInt2 := int32(2)
myIntSum := SumGenerics(myInt1, myInt2)
println(myIntSum)
myFloat1 := float32(1.1)
myFloat2 := float32(2.2)
myFloatSum := SumGenerics(myFloat1, myFloat2)
println(myFloatSum)
}
// ## struct serialize, deserialize
type Person struct {
Name string
Age int
Address string
}
func Serialize[T any](data T) ([]byte, error) {
buffer := bytes.Buffer{}
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(data)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func Deserialize[T any](b []byte) (T, error) {
buffer := bytes.Buffer{}
buffer.Write(b)
decoder := gob.NewDecoder(&buffer)
var data T
err := decoder.Decode(&data)
if err != nil {
return data, err
}
return data, nil
}
func SerializeUsage() {
person := Person{
Name: "John",
Age: 30,
Address: "123 Main St.",
}
serialized, err := Serialize(person)
if err != nil {
panic(err)
}
println(serialized)
}
func DeserializeUsage() {
person := Person{
Name: "John",
Age: 30,
Address: "123 Main St.",
}
serialized, err := Serialize(person)
if err != nil {
panic(err)
}
deserialized, err := Deserialize[Person](serialized)
if err != nil {
panic(err)
}
fmt.Printf("Name: %s, Age: %d, Address: %s", deserialized.Name, deserialized.Age, deserialized.Address)
}
// ## validators
type Validator[T any] func(T) error
func Validate[T any](data T, validators ...Validator[T]) error {
for _, validator := range validators {
err := validator(data)
if err != nil {
return err
}
}
return nil
}
func StringNotEmpty(s string) error {
if len(strings.TrimSpace(s)) == 0 {
return fmt.Errorf("string cannot be empty")
}
return nil
}
func IntInRange(num int, min, max int) error {
if num < min || num > max {
return fmt.Errorf("number must be between %d and %d", min, max)
}
return nil
}
func ValidateUsage() {
person := Person{
Name: "John",
Age: 30,
Address: "123 Main St.",
}
err := Validate(person, func(p Person) error {
return StringNotEmpty(p.Name)
}, func(p Person) error {
return IntInRange(p.Age, 0, 120)
})
if err != nil {
println(err.Error())
panic(err)
}
println("Person is valid")
}
type LoginForm struct {
Username string
Password string
}
func (f *LoginForm) Validate() error {
return Validate(f,
func(l *LoginForm) error {
return StringNotEmpty(l.Username)
},
func(l *LoginForm) error {
return StringNotEmpty(l.Password)
},
)
}
func ValidateUsage2() {
loginForm := LoginForm{
Username: "John",
Password: "123",
}
err := loginForm.Validate()
if err != nil {
println(err.Error())
panic(err)
}
println("Login form is valid")
}
// ## example of using generics with interfaces and pointers
type WalletData struct {
Address string
WalletInfo interface{}
}
func ValidateAddress[T any](wallet *WalletData) bool {
// Check if the wallet address is non-empty
if wallet.Address != "" {
// Check if the wallet info type matches the generic type T
if reflect.TypeOf(wallet.WalletInfo) == reflect.TypeOf((*T)(nil)).Elem() {
return true
}
}
return false
}
func ValidateAddressUsage() {
wallet := &WalletData{
Address: "123 Main St.",
WalletInfo: "123",
}
// Check if the wallet address is non-empty
if ValidateAddress[string](wallet) {
println("Wallet address is valid")
} else {
println("Wallet address is invalid")
}
}
// ## example of using generic function to retrieve data from pointer struct and validate it
type User struct {
Username string
Password string
}
func (u *User) Validate() error {
return Validate(u,
func(u *User) error {
return StringNotEmpty(u.Username)
},
func(u *User) error {
return StringNotEmpty(u.Password)
},
)
}
func ValidateUser[T any](user *User) bool {
// Check if the user is non-empty
if user != nil {
// Check if the user type matches the generic type T
if reflect.TypeOf(user).Elem() == reflect.TypeOf((*T)(nil)).Elem() {
return true
}
}
return false
}
func ValidateUserUsage() {
// validates a user
user := &User{
Username: "John",
Password: "123",
}
if ValidateUser[User](user) {
println("User is valid")
} else {
println("User is invalid")
}
// validates a nil user
var nilUser *User
if ValidateUser[User](nilUser) {
println("User is valid")
} else {
println("User is invalid")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment