Skip to content

Instantly share code, notes, and snippets.

@sug0
Last active March 14, 2024 07:10
Show Gist options
  • Save sug0/36b8f10be0a33216cb1ba32dcb58df03 to your computer and use it in GitHub Desktop.
Save sug0/36b8f10be0a33216cb1ba32dcb58df03 to your computer and use it in GitHub Desktop.
Go 1.18 tagged union types!
// no, this is not that useful
package main
import (
"fmt"
"unsafe"
)
type Kind[T any] struct{}
type EitherLeft[L any] struct{}
type EitherRight[R any] struct{}
type Either[L, R any] interface {
EitherLeft[L] | EitherRight[R]
}
type Union[K any] struct {
kind Kind[K]
data []byte
}
type EitherUnion[L, R any, K Either[L, R]] Union[K]
func main() {
u1 := Left[string, int32]("ganda cena mano")
// `Kind` is a zero size type
assert(unsafe.Sizeof(u1) == unsafe.Sizeof([]byte{}))
s := *u1.Left()
fmt.Printf("Union: %#v\n", u1)
fmt.Printf("Stored: %s\n", s)
fmt.Println("---------------------")
u2 := Right[struct{}, uint16](420)
x := *u2.Right()
fmt.Printf("Union: %#v\n", u2)
fmt.Printf("Stored: %d\n", x)
fmt.Println("---------------------")
for _, u := range []any{u1, u2} {
switch u.(type) {
case EitherUnion[string, int32, EitherLeft[string]]:
fmt.Println("string")
case EitherUnion[struct{}, uint16, EitherRight[uint16]]:
fmt.Println("uint16")
}
}
}
func Left[L, R any](left L) EitherUnion[L, R, EitherLeft[L]] {
u := either[L, R, EitherLeft[L]]()
*(*L)(unsafe.Pointer(&u.data[0])) = left
return u
}
func Right[L, R any](right R) EitherUnion[L, R, EitherRight[R]] {
u := either[L, R, EitherRight[R]]()
*(*R)(unsafe.Pointer(&u.data[0])) = right
return u
}
func (u *EitherUnion[L, R, K]) Left() *L {
switch any(u.kind).(type) {
case Kind[EitherLeft[L]]:
return (*L)(unsafe.Pointer(&u.data[0]))
case Kind[EitherRight[R]]:
return nil
}
return nil
}
func (u *EitherUnion[L, R, K]) Right() *R {
switch any(u.kind).(type) {
case Kind[EitherLeft[L]]:
return nil
case Kind[EitherRight[R]]:
return (*R)(unsafe.Pointer(&u.data[0]))
}
return nil
}
func either[L, R any, K Either[L, R]]() EitherUnion[L, R, K] {
var (
maxSize uintptr
left L
right R
)
if unsafe.Sizeof(right) > unsafe.Sizeof(left) {
maxSize = unsafe.Sizeof(right)
} else {
maxSize = unsafe.Sizeof(left)
}
return EitherUnion[L, R, K]{
data: make([]byte, maxSize),
}
}
func assert(cond bool) {
if !cond {
panic("Assertion failed")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment