Last active
March 14, 2024 07:10
-
-
Save sug0/36b8f10be0a33216cb1ba32dcb58df03 to your computer and use it in GitHub Desktop.
Go 1.18 tagged union types!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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