For normal values, just instantiate the T
directly to get its value and reflect.TypeOf()
it. But we can declare a [0]T
and take its element type instead, because:
[0]T
takes 0 memory while T
may be as large as T
, which would waste a lot of stack and heap 1 if T
is something like [4096]int
.
- A raw
T
does not work with interface types. reflect.TypeOf()
on a nil interface value (whether empty interface or not) will return reflect.Type(nil)
, of which subsequent uses will cause panic.
package main
import (
"fmt"
"reflect"
)
type MyGeneric[T any] struct {
}
func (mG MyGeneric[T]) canHandle(value any) bool {
var zero [0]T
tt := reflect.TypeOf(zero).Elem()
vt := reflect.TypeOf(value)
fmt.Printf("-> %v covers %v\n", tt, vt)
return vt.AssignableTo(tt)
}
type empty struct{}
func main() {
fmt.Printf("%v\n", MyGeneric[string]{}.canHandle(""))
fmt.Printf("%v\n", MyGeneric[any]{}.canHandle(""))
fmt.Printf("%v\n", MyGeneric[string]{}.canHandle(1))
fmt.Printf("%v\n", MyGeneric[MyGeneric[struct{}]]{}.canHandle(MyGeneric[struct{}]{}))
fmt.Printf("%v\n", MyGeneric[MyGeneric[struct{}]]{}.canHandle(MyGeneric[empty]{}))
}
Output:
-> string covers string
true
-> interface {} covers string
true
-> string covers int
false
-> main.MyGeneric[struct {}] covers main.MyGeneric[struct {}]
true
-> main.MyGeneric[struct {}] covers main.MyGeneric[main.empty]
false
1 Not sure if it can be optimized out or whether it allocates on heap or stack or both, because it is passed into reflect.TypeOf
after upcasting to an interface{}
.
T
is instantiated with interfaces. – Shammy