I am trying to write the following function:
func Fill[X any](slice []*X){
for i := range slice {
slice[i] = new(X)
}
}
xs := make([]*int, 10) // fill with nils
Fill(xs) // now fill with new(int)
That works fine but… if I want to use a slice of interfaces and provide a concrete type?
func Fill[X, Y any](slice []X){
for i := range slice {
slice[i] = new(Y) // not work!
}
}
xs := make([]sync.Locker, 10) // fill with nils
Fill[sync.Locker,sync.Mutex](xs) // ouch
I try some combinations without success, is there a way or go1.18 does not support such relations?
CodePudding user response:
When you constrain both X
and Y
to any
, you lose all interface-implementor relationship. The only thing that is known at compile time is that X
and Y
are different types, and you can't assign one to the another within the function body.
A way to make it compile is to use an explicit assertion:
func Fill[X, Y any](slice []X) {
for i := range slice {
slice[i] = any(*new(Y)).(X)
}
}
But this panics if Y
doesn't really implement X
, as in your case, since it is *sync.Mutex
(pointer type) that implements sync.Locker
.
Moreover, when Y
is instantiated with a pointer type, you lose information about the base type, and therefore the zero value, including *new(Y)
would be nil
, so you don't really have a baseline improvement over make
(just typed nils vs. nil interfaces).
What you would like to do is to constrain Y
to X
, like Fill[X any, Y X](slice []X)
but this is not possible because 1) a type parameter can't be used as a constraint; and/or 2) a constraint can't embed a type parameter directly. It also initializes nils as the above.
A better solution is to use a constructor function instead of a second type parameter:
func main() {
xs := make([]sync.Locker, 10)
Fill(xs, func() sync.Locker { return &sync.Mutex{} })
}
func Fill[X any](slice []X, f func() X) {
for i := range slice {
slice[i] = f()
}
}