-1

I'm working on a little side projects, and I want to have a slice in Go, with any amount of elements with a different type parameter.

Is something like that possible?

I've tried the following:

func main() {
    fCallOne := functionCall[bool]{
        returnValue: false,
    }

    fCallTwo := functionCall[int]{
        returnValue: 0,
    }

    fCalls := make([]functionCall[any], 10)
    fCalls = append(fCalls, fCallOne)
}

type functionCall[T any] struct {
    returnValue T
}

But this doesn't work. I would like to be able to do something like:

Store an amount of elements in a slice (all sharing the same API). Retrieve the value of a given index in the slice (without any type casting).

What would be the best approach to pull something like this off?

Currently, I'm down to the following but this doesn't work either:

func main() {
    container := make([]tuple[any], 0)
    elem := T1[bool]{
        V1: false,
    }

    container = append(container, elem)
}

type tuple[T any] interface {
    getValue() T
}

// T1 is a tuple type holding 1 generic values.
type T1[Ty1 any] struct {
    V1 Ty1
}

func (t T1[Ty1]) getValue() Ty1 {
    return t.V1
}
Complexity
  • 5,682
  • 6
  • 41
  • 84
  • No. Use interfaces, not generics. – Volker Jun 15 '23 at 15:40
  • You mean, an actual interface, or an `interface{}`? – Complexity Jun 15 '23 at 17:13
  • The empty interface `interface{}` _is_ an "actual interface". Make a slice of an _appropriate_ interface type (I cannot judge what that would look like from the code snippet you showed). It's just that "generics" don't work like you want them to and the normal solution is to define an appropriate interface capturing behaviour and use a slice of those. – Volker Jun 15 '23 at 18:00
  • Your actual constraint here is the `any` interface, so even if you could create specialized types to accommodate your use case, the fact that the constraint is `any` means you have no information about what that specialized type is, which means you are left to runtime type assertions (there is no type casting). – JimB Jun 15 '23 at 18:09

1 Answers1

0

In your first attempt, any in the context of the line:

fCalls := make([]functionCall[any], 10)
                              ^^^

is not a constraint but a type argument to your generic type functionCall. Here, you are creating a slice whose elements are of type functionCall[any]. The fCallOne value you want to append to this slice is of type functionCall[bool] and not functionCall[any], which is why your following line doesn't compile:

fCalls = append(fCalls, fCallOne)

However, since you can assign any value to an any interface, you can assign false and 0 to an any field:

fCallAny1 := functionCall[any] {
    returnValue: false,
}

fCallAny2 := functionCall[any] {
    returnValue: 0,
}

fCalls := make([]functionCall[any], 10)
fCalls = append(fCalls, fCallAny1, fCallAny2)

I would like to be able to do something like: Store an amount of elements in a slice (all sharing the same API).

If all the elements share the same API, for example:

type foo int
type bar string

func (foo) doSomething(){}
func (bar) doSomething(){}

Then, you can define an interface that represents the shared API:

type sharedAPI interface {
    doSomething()
}

Finally, you can create a slice of sharedAPIs and append the values that satisfy this interface:

container := make([]sharedAPI, 2)
container = append(container, foo(0), bar("hi"))

You will have access to the shared API on every element of the slice:

container[0].doSomething()
container[1].doSomething()

Retrieve the value of a given index in the slice (without any type casting).

This is not possible because part of the static type information is lost: you no longer have elements of type foo and bar but only of sharedAPI. Besides, all slice elements must have the same static type (all are sharedAPI). You must use type assertions or type switches to leverage the dynamic type information that the interface sharedAPI contains.

JFMR
  • 23,265
  • 4
  • 52
  • 76