Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get type parameter from a generic struct using reflection

Imagine I have the following struct:

type MyGeneric[T string | int] struct {
}

I want to check whether the generic used to instantiate that struct was a string or a int when creating a new MyGeneric.

myGenericString := MyGeneric[string]{}
myGenericString.canHandle("hello") -> should return true
myGenericString.canHandle(8) -> should return false

func (mG MyGeneric[T]) canHandle(value any) bool {
    // how to get what T is the same type as value
}

like image 916
Manuelarte Avatar asked May 08 '26 17:05

Manuelarte


1 Answers

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{}.

like image 54
SOFe Avatar answered May 10 '26 08:05

SOFe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!