I have the following code with two generic functions. They both do the simple operation of appending a slice to itself and returning the result. However, invoking the simpler and more straightforward function cat1
produces a compilation error while invoking the more clumsily defined function cat2
with its superfluous and explicit declaration of the slice element E
works as expected. Why isn't cat1
acceptable?
func cat1[S ~[]any](s S) S {
return append(s, s...)
}
func cat2[S ~[]E, E any](s S) S {
return append(s, s...)
}
func main() {
fmt.Println(string(cat2([]byte("hello"))))
}
The error when invoking cat1
is:
[]byte does not satisfy ~[]any ([]byte missing in ~[]any)
Type constraints must be interfaces, however the keyword interface
is almost always omitted as a syntactic sugar.
Therefore when you use ~[]any
as a type constraint, what you're actually writing is:
interface{ ~[]any }
In this interface, ~[]any
is a type term. What types can satisfy this constraint? As with all constraints:
A type argument T satisfies a type constraint C if T is an element of the type set defined by C
The type set defined by your constraint C
a.k.a. interface{ ~[]any }
is the type literal []any
and all types with []any
as underlying type. For example:
type Foo []any
[]byte
is not included in the type set of interface{ ~[]any }
, just like outside of generic code you couldn't assign a []byte
to an []any
:
var a []byte
var b []any
b = a // compilation error
As @JimB mentioned in a comment, byte
and any
have different memory layouts, therefore their composite types aren't mutually assignable.
Now when you use any
alone as a type constraint, things change. any
is an alias of interface{}
(the empty interface). In this case there is no syntactic sugar. The constraint is just interface{}
which is an interface with no methods and no type terms. All types implement it, including byte
.
Your second function cat2
is the only correct way to define a type parameter S
that can be any slice:
func cat2[S ~[]E, E any](s S) S {
return append(s, s...)
}
Here E
is constrained to any type, and S
resolves to a slice of whatever E
is.