generics: parametric types unmarshaling

362 views
Skip to first unread message

Sebastien Binet

unread,
Apr 14, 2022, 4:51:22 AM4/14/22
to golang-nuts
hi there,

I am playing a bit with generics.
I am trying to implement a parametrized "Read" function that unmarshals
bytes into some type value that implements an interface:

type T1 struct {
v string
}

func (t *T1) UnmarshalBinary(p []byte) error {
t.v = string(p)
return nil
}

type Unmarshaler interface {
UnmarshalBinary(p []byte) error
}

func f1[T Unmarshaler](p []byte) (T, error) {
var t T
err := t.UnmarshalBinary(p)
return t, err
}

func main() {
p := []byte("hello")
t1, err := f1[T1](p)
if err != nil {
log.Panic(err)
}

log.Printf("t1: %+v", t1)
}

my naive attempt failed like so:

./prog.go:26:16: T1 does not implement Unmarshaler (UnmarshalBinary method has pointer receiver)

it's only when I rewrite my f1 function like so that it "works":

func f2[T any](p []byte) (t T, err error) {
switch t := any(&t).(type) {
case Unmarshaler:
err = t.UnmarshalBinary(p)
default:
panic("boo")
}
return t, err
}

but it doesn't take advantage of the nice type constraints Go's generics
provide.

of course, replacing:
t1, err := f1[T1](p)

with:
t1, err := f1[*T1](p)
compiles. but fails at runtime.

what am I missing?
how do I convey the constraint "unmarshaling usually imply passing a
pointer to some value" ?

-s

Zhaoxun Yan

unread,
Apr 15, 2022, 2:00:50 AM4/15/22
to golang-nuts
Here is my note on json & struct, run it somewhere and it may give you a hint.

package main

import "fmt"
import "encoding/json"

type prices struct{
    Price   float64 `json:"price"`
    floor   float64
    Ceiling float64
    settle  float64
    time    int64
}


func main() {
    content := prices{
        Price:  6.4 ,
        floor:  3.5,
        Ceiling:    7,
        settle: 8.5,
        time:   1,
    }
   
    js,_ := json.Marshal( content)
    fmt.Println(string(js))

    var back prices
    json.Unmarshal(js, &back)
    fmt.Println(back)
    fmt.Println(back.Price)
}

Sebastien Binet

unread,
Apr 15, 2022, 4:20:15 AM4/15/22
to Zhaoxun Yan, golang-nuts
hi,
thanks for the offer.
but that's not really what I am after.

my issue is two-fold:
- how to convey the constraint of the Unmarshaler interface (which is
logically implemented on a pointer receiver, so *T, not T)
- how to create values of T (where *T implements Unmarshaler)

I have managed to something along these lines, with some reflect help:

https://go.dev/play/p/-CK2Bk-LU8y

```
type T1 struct {
v string
}

func (t *T1) Unmarshal(p []byte) error {
t.v = string(p)
return nil
}

type Unmarshaler interface {
Unmarshal(p []byte) error
}

func f[T Unmarshaler]() T {
t := alloc[T]()
unmarshal(t)
return t
}

func alloc[T any]() T {
var t T
rv := reflect.ValueOf(&t).Elem()
rv.Set(reflect.New(rv.Type().Elem()))
return t
}

func unmarshal[T Unmarshaler](t T) {
err := t.Unmarshal([]byte("hello"))
if err != nil {
panic(err)
}
}

func main() {
t := *f[*T1]()
log.Printf("t1: %+v", t)
}
```

so: passing *T to the "factory function", and using reflect to reach
into the guts of underlying type, allocate and return the newly
populated value.

it's not super robust.
but I can do.

perhaps I am not holding it right?
I am probably missing the type manipulation vocabulary (akin to
reflect's PointerTo and Elem/Indirect facilities).

-s

roger peppe

unread,
Apr 15, 2022, 4:52:32 AM4/15/22
to Sebastien Binet, golang-nuts
This is a classic use case for structural type constraints: https://go.dev/play/p/-stEjeeqQD0

-s

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CJ9U1G7RBSM9.I0L5BA20IVD1%40clrinfopc42.

Sebastien Binet

unread,
Apr 15, 2022, 5:35:06 AM4/15/22
to roger peppe, golang-nuts
On Fri Apr 15, 2022 at 10:52 CET, roger peppe wrote:
> > what am I missing?
> > how do I convey the constraint "unmarshaling usually imply passing a
> > pointer to some value" ?
> >
>
> This is a classic use case for structural type constraints:
> https://go.dev/play/p/-stEjeeqQD0

thanks a lot!

adding "structural type constraints" to my generics vocabulary :)

-s
Reply all
Reply to author
Forward
0 new messages