'How to iterate over a union of slices passed in a generic function? (T has no core type)

I am testing out generics in go 1.18 and took a look at this example. I would like to recreate that example but instead be able to pass in a slice of int or slice of float instead, and in the function I'll just sum up everything in the slice.

This is when I ran into some issues just iterating the slice. This is what I tried:

package main

import "fmt"

// NumberSlice constraint
type NumberSlice interface {
    []int64 | []float64
}

func add[N NumberSlice](n N) {
    // want: to range over n and print value of v 
    for _, v := range n {
        fmt.Println(v)
    }
}

func main() {
    ints := []int64{1, 2}
    add(ints)
}

I got the error:

cannot range over n (variable of type N constrained by NumberSlice) (N has no core type)

How do I accomplish this?



Solution 1:[1]

A core type, for an interface (including an interface constraint) is defined as follows:

An interface T has a core type if one of the following conditions is satisfied:

  • There is a single type U which is the underlying type of all types in the type set of T

  • or the type set of T contains only channel types with identical element type E, and all directional channels have the same direction.

Your interface constraint has no core type, because it has two underlying types: []int64 and []float64.

Therefore you can't use it where a core type is required. Notably range and make.

You can change the interface to require the base types, and then specify the slice in the function signature:

// still no core type...
type Number interface {
    int64 | float64
}

// ...but the argument will be instantiated with either int64 or float64
func add[N Number](n []N) {
    for _, v := range n {
        fmt.Println(v)
    }
}

This also works, but it's way more verbose:

type NumberSlice[N int64 | float64] interface {
    // one core type []N
    ~[]N
}

func add[S NumberSlice[N], N int64 | float64](n S) {
    for _, v := range n {
        fmt.Println(v)
    }
}

Solution 2:[2]

Could something like this work for you?

package main

import "fmt"

type NumberOrFloat interface {
    int64 | float64
}

func add[N NumberOrFloat](n []N) {
    for _, v := range n {
        fmt.Println(v)
    }
}

func main() {
    ints := []int64{1, 2}
    add(ints)
}

The difference here is that you define type constraints on array elements (not on array types): []N

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2