Home > Software design >  Generic function accepting both channels and slices
Generic function accepting both channels and slices

Time:12-05

I'm trying to write generic function in Golang that would search for a value in slices and in channels in the similar way. Here is an example:

// MinOf returns the smallest number found among the channel / slice contents
func MinOf[T chan int | []int](input T) (result int) {
    for _, value := range input {
        if result > value {
            result = value
        }
    }

    return
}

But I'm getting following compilation error: cannot range over input (variable of type T constrained by chan int|[]int) (T has no core type).

I have tried to create common interface, like so:

type Rangable interface {
    chan int | []int
}

// MinOf returns the smallest number found among the channel / slice contents
func MinOf[T Rangable](input T) (result int) {
    for _, value := range input {
        if result > value {
            result = value
        }
    }

    return
}

Though, error has changed to cannot range over input (variable of type T constrained by Rangable) (T has no core type) it remains basically the same...

Is there any way how to solve this task using generics or channels and slices could not be "casted" to same core type?

Thank you for any suggestions and ideas!

CodePudding user response:

You can't do this.

The range expression must have a core type to begin with. Unions with diverse type terms, do not have a core type because there isn't one single underlying type in common.

You can also intuitively see why range requires a core type: the semantics of ranging over slices and channels are different.

  1. Ranging over a channel is potentially a blocking operation, ranging over a slice isn't

  2. The iteration variables are different

for i, item := range someSlice {}

With slices i is the index of type int and item is the type of the slice elements.

for item := range someChan {}

With channels, item is the type of the chan elements and that's the only possible range variable.

The best you can have is a type switch:

func MinOf[T any, U chan T | []T](input U) (result int) {
    switch t := any(input).(type) {
    case chan T:
        // range over chan
    case []T:
        // range over slice
    }
    return
}

But again, the behavior of this function (blocking vs. non-blocking) is type dependant, and it's unclear what advantages you get by using generics here.

  • Related