3

I'm trying to simplify some code using generics. Even though I currently have a bunch of types (A, B, C) that each implement their own data-ingestion, the logic is the same; so I want to combine them using generics.

The general idea is that the data comes in an abstract format, and gets unmarshaled from json into the correct type:

func unmarshalA(json Json) *A {
  rv := &A{}
  Unmarshall(json, rv)
  rv.Validate()
  return rv
}

Of course error handling and the likes is omitted here.

Now since each of these have the same logic, what I'd like to do is the following:

type canBeValidated interface {
  Validate() error
}

func unmarshalType[T canBeValidated](json Json) *T { 
  rv := &T{}
  Unmarshall(json, rv)
  rv.Validate()         // Unresolved Reference: Validate
  return rv
}

So my question then becomes: How do I use generic types (or another solution) to mandate that a type has a particular method; without caring about anything else?

Mitchell Faas
  • 430
  • 6
  • 19
  • 3
    Slices (of any kind) don't have methods. That's why you can't call Validate. To "mandate that a type has a particular method" use the interface you have already defined. You don't need generics for that. – Peter Jun 24 '22 at 11:51
  • @Peter I'm having a little bit of trouble understanding how that would work. If we were to use plain interfaces, how do we make the assignment at `rv := &T{}`? It seems like we wouldn't have information on the underlying type, and thereby would be unable to perform the unmarshaling. – Mitchell Faas Jun 24 '22 at 11:55
  • 2
    You would accept a (pointer to a) value of a concrete type as an argument. Exactly how json.Unmarshal works. – Peter Jun 24 '22 at 11:58
  • 1
    https://go.dev/play/p/esVOo67tmKJ – mkopriva Jun 24 '22 at 12:00
  • The linked question shows how to do this with generics specifically but as Peter said, it looks like you’re better just using plain interfaces without type parameters – blackgreen Jun 24 '22 at 13:28

0 Answers0