Created
December 30, 2022 15:39
-
-
Save valsteen/40a1b0fa8eda11d07764aebaf4524924 to your computer and use it in GitHub Desktop.
Option generics with (un)marshalling
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
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
) | |
type TenantProfileEntity struct { | |
SomeOtherValue string | |
} | |
type MyType struct { | |
Name string | |
Description string | |
MyVarGotByFK Option[TenantProfileEntity] | |
} | |
type Option[T any] struct { | |
isSet bool | |
inner T | |
} | |
func (o Option[T]) MarshalJSON() ([]byte, error) { | |
if obj := o.Unwrap(); obj != nil { | |
if inner, ok := any(&o.inner).(json.Marshaler); ok { | |
return inner.MarshalJSON() | |
} | |
return json.Marshal(obj) | |
} | |
return []byte("null"), nil | |
} | |
func (o *Option[T]) UnmarshalJSON(bytes []byte) error { | |
if string(bytes) == "null" { | |
o.isSet = false | |
return nil | |
} | |
if inner, ok := any(&o.inner).(json.Unmarshaler); ok { | |
return inner.UnmarshalJSON(bytes) | |
} | |
if err := json.Unmarshal(bytes, &o.inner); err != nil { | |
return err | |
} | |
o.isSet = true | |
return nil | |
} | |
func (o *Option[T]) Unwrap() *T { | |
if o.isSet { | |
return &o.inner | |
} | |
return nil | |
} | |
func None[T any]() Option[T] { | |
return Option[T]{} | |
} | |
func Some[T any](obj T) Option[T] { | |
return Option[T]{ | |
inner: obj, | |
isSet: true, | |
} | |
} | |
func Get(id int) Option[MyType] { | |
switch id { | |
case 0: | |
return None[MyType]() | |
case 1: | |
return Some(MyType{ | |
Name: "MyVarGotByFK not set", | |
}) | |
default: | |
return Some(MyType{ | |
MyVarGotByFK: Some(TenantProfileEntity{SomeOtherValue: "hi"}), | |
}) | |
} | |
} | |
func main() { | |
// below escape analysis results on go1.19.4 , using go build -gcflags=-m=2 | |
for i := 0; i < 3; i++ { | |
obj := Get(i) // won't escape | |
if myObj := obj.Unwrap(); myObj != nil { // won't escape | |
fmt.Printf("myObj %d is set\n", i) // i escapes due to Printf | |
if profile := myObj.MyVarGotByFK.Unwrap(); profile != nil { // won't escape | |
// note : profile.SomeOtherValue escapes to the heap due to Printf | |
fmt.Printf("profile of %d is set: %s\n", i, profile.SomeOtherValue) | |
} | |
} else { | |
fmt.Printf("myObj %d is not set\n", i) | |
} | |
marshalled, err := json.Marshal(obj) | |
if err != nil { | |
fmt.Println("marshalling error", err) | |
} else { | |
fmt.Printf("mashalled: %s\n", string(marshalled)) | |
} | |
var unmarshalled Option[MyType] | |
if err := unmarshalled.UnmarshalJSON(marshalled); err != nil { | |
fmt.Println("unmarshalling error", err) | |
} else { | |
fmt.Printf("unmarshalled: %+v\n", unmarshalled) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment