0

We want to have

type ResponseListDataPayload struct {
    List     []*interface{} `json:"list"` //generic
    TotalCnt int64          `json:"totalCnt"`
    Page     int64          `json:"page"`
    Step     int64          `json:"step"`
}

and List could would accept []*model.SomeModel{}

queryResults := []*model.SomeModel{}
resposeResult := &ResponseListDataPayload{
    List:     queryResults,
    TotalCnt: cnt,
    Page:     pageInt,
    Step:     stepInt,
}

or []*model.AnotherModel{}

queryResults := []*model.AnotherModel{}
resposeResult := &ResponseListDataPayload{
    List:     queryResults,
    TotalCnt: cnt,
    Page:     pageInt,
    Step:     stepInt,
}

That's pretty straightforward in Java, could that be possible in go?

blackgreen
  • 34,072
  • 23
  • 111
  • 129
armnotstrong
  • 8,605
  • 16
  • 65
  • 130
  • 3
    I'm pretty sure you want `[]interface{}` and not `[]*interface{}`. An `interface{}` can contain a pointer. – super Sep 15 '21 at 13:17
  • 4
    Also, have a look at [this](https://stackoverflow.com/questions/12753805/type-converting-slices-of-interfaces). – super Sep 15 '21 at 13:19
  • 6
    `interface{}` means `interface{}`, not "any type". – Volker Sep 15 '21 at 13:37
  • 1
    just use an empty interface. some links to read about interface inernals. https://golangbyexample.com/inner-working-interface-golang/ https://github.com/teh-cmc/go-internals/blob/master/chapter2_interfaces/README.md#anatomy-of-an-interface and here rsc gives a more detailed explanation https://research.swtch.com/interfaces and also this one https://eli.thegreenplace.net/2021/go-internals-invariance-and-memory-layout-of-slices/ –  Sep 16 '21 at 17:11

2 Answers2

12

Go 1.18

You can now have parametrized structs:

type ResponseListDataPayload[T any] struct {
    List     []T            `json:"list"` //generic
    TotalCnt int64          `json:"totalCnt"`
    Page     int64          `json:"page"`
    Step     int64          `json:"step"`
}

Remember that generic structs must be instantiated with an explicit type parameter:

queryResults := []*model.SomeModel{}
responseResult := &ResponseListDataPayload[*model.SomeModel]{
    List:     queryResults,
    // other fields
}

If you want to improve code reuse even more and "genericize" also the struct initialization, you can use a constructor function. Functions can take advantage of type inference to omit writing out the type arguments:

// add to func signature other arguments as needed
func NewResponseFor[T any](list []T) *ResponseListDataPayload[T] {
    return &ResponseListDataPayload[T]{ List: list }
}

and use it as:

queryResults := // some query results
responseResult := NewResponseFor(queryResults)

Example: https://gotipplay.golang.org/p/jYTHegaeubR


Go 1.17 and below

could that be possible in go?

No, interface{} is not actually a generic type, it's just an interface with an empty method set.

Formally, you can assign any concrete value to it because assignability requires the value's method set to be a superset of the interface method set, and any set is a superset of the empty set ().

It's not the same thing as a parametrized type like Java's List<T> hence in Go []Foo is not assignable to []interface{}.

You must process the single elements in a loop:

var m []*model.anotherModel
// populate m
for _, v := range m {
    resp.List = append(resp.List, v)
}

Similarly, don't use pointers to the empty interface *interface{}. Use interface{} instead.

If your goal is to just serialize JSON (based on the presence of the tags on your struct), you can declare the List field as interface{} and you’ll be able to assign either of your slice values to it, for the reason stated above. Thus avoiding an extra slice manipulation. The json package will then serialize based on the concrete values boxed in the interface{}.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
2

In Go 1.18 you will be able to do something like, with real go generics:

func convert[S any](src []S) []interface{} {
    dst := make([]interface{}, 0, len(src))
    for _, v := range src {
        dst = append(dst, v)
    }
    return dst
}
//...
resp.List = convert(m)

But until 1.18 is out and more codebases embrace generics, you will still need to do the conversion by hand.

And like everyone said, don't use *interface{}.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • thanks, could you explain why `*interface{}` is not recommended? – armnotstrong Sep 16 '21 at 06:18
  • 1
    @armnotstrong because it'd be a pointer to an interface, so for example to get the value of it you'd have to do something like `(*x[0]).(type)`, which is redundant and not needed. – OneOfOne Sep 16 '21 at 06:41
  • 1
    @armnotstrong an interface, including the empty one `interface{}` is implemented as a data structure with a reference to the value of the stored type and a reference to the actual data. Which could very well be a pointer already. So having a pointer to this data structure as `*interface{}` is simply not useful. (And forces you to redundant dereference to do anything with it as OneOfOne said) – blackgreen Sep 16 '21 at 09:31