With the introduction of weak pointers in Go 1.24, I’ve been exploring their use cases. I’ve found a situation where I need to create and manage weak pointers dynamically, based on types determined at runtime – essentially, I need reflection capabilities for weak pointers.
The current weak.Pointer is generic, and as far as I can tell, Go doesn’t currently support calling generic functions via reflection. This makes it impossible to create a weak.Pointer[T] when T is only known as a reflect.Type.
I’ve looked through the issue tracker and the golang-nuts mailing list, and I haven’t found any existing discussions directly addressing this. Before I submit a formal proposal on GitHub, I wanted to gauge the community’s opinion on whether adding reflection support for weak pointers would be a valuable addition to the language.
Here’s a rough sketch of what I envision the API might look like (using a hypothetical weak.PointerAny):
package weak // or perhaps a new package like reflect/weak
type PointerAny struct {
typ reflect.Type
u unsafe.Pointer
}
func MakeAny(ptr any) PointerAny {
ptr = abi.Escape(ptr)
v := reflect.ValueOf(ptr)
t := v.Type()
if t.Kind() != reflect.Pointer {
panic("ptr is not a pointer")
}
var u unsafe.Pointer
if ptr != nil {
u = runtime_registerWeakPointer(v.UnsafePointer())
}
runtime.KeepAlive(ptr)
return PointerAny{typ: t.Elem(), u: u}
}
func (p PointerAny) Value() any {
if p.u == nil {
return nil
}
ptr := runtime_makeStrongFromWeak(p.u)
if ptr == nil {
return nil
}
return reflect.NewAt(p.typ, ptr).Interface()
}
This PointerAny would allow creating a weak pointer from an any (which must be a pointer) and retrieving the original value as an any.
Is there a compelling reason not to add this? Would adding reflection support for weak pointers introduce significant complexity or have unintended consequences? Perhaps there are fundamental reasons why this hasn’t been implemented already.
IMHO, weak pointer does not need to know the exact type of the value. Reflect is always very costly and I doubt it’s a common use. You can always simply add your own wrapper around weak pointer with its type:
type MyPointer struct {
t reflect.Type
p weak.Pointer
}
That wrapper doesn’t solve the generic type issue for reflection. I still get the cannot use generic type weak.Pointer[T any] without instantiation error.
Yeah, because reflection and generics operate in different fields. One is for comptime another is for runtime. You can also try to create pointer to any value and pass it to weak pointer creation, something like:
func Pointer[T any](val T) *T {
v := reflect.ValueOf(val)
if !v.CanAddr() {
v = reflect.ValueOf(&val)
}
ptr, ok := v.Interface().(*T)
if !ok {
panic("")
}
return ptr
}
I need to call a Make generic function to create weak pointers when I only have a reflect.Value, which isn’t possible with the current code. It might look something like this:
func MakeAny(ptr any) PointerAny
If you mean bypassing the type like this, it will cause an internal error.
package main
import (
"fmt"
"reflect"
)
type WeaKPointer struct{}
func MakeAny(ptr any) WeaKPointer {
panic("not implemented")
}
func (p WeaKPointer) Value() any {
panic("not implemented")
}
var instances = make(map[string]WeaKPointer)
// ExampleFunc creates a weak pointer for all pointer types in the struct and saves them to the global variable instances.
// This function is called by others in a library, and we cannot know the type of T in advance.
func ExampleFunc[T any](val T) {
rfType := reflect.TypeOf(val)
rfValue := reflect.ValueOf(val)
if rfType.Kind() != reflect.Struct {
panic("T must be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Field(i)
valueField := rfValue.Field(i)
if typeField.Type.Kind() == reflect.Pointer {
instances[typeField.Name] = MakeAny(valueField.Interface())
}
}
}
type Struct1 struct {
Field1 *int
Field2 *string
}
func main() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
ExampleFunc(s1)
fmt.Println(instances)
}
package main
import (
"fmt"
"reflect"
"weak"
)
var instances = make(map[string]weak.Pointer[reflect.Value], 0)
// ExampleFunc creates a weak pointer for all pointer types in the struct and saves them to the global variable instances.
// This function is called by others in a library, and we cannot know the type of T in advance.
func ExampleFunc[T any](val T) {
rfType := reflect.TypeOf(val)
rfValue := reflect.ValueOf(val)
if rfType.Kind() != reflect.Struct {
panic("T must be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Field(i)
valueField := rfValue.Field(i)
if typeField.Type.Kind() == reflect.Pointer {
instances[typeField.Name] = weak.Make(&valueField)
}
}
}
type Struct1 struct {
Field1 *int
Field2 *string
}
func main() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
*s1.Field1 = 5
*s1.Field2 = "test"
ExampleFunc(s1)
for n, v := range instances {
fmt.Println(n, v.Value())
}
}
Here, Make only creates weak pointers to reflect.Value, not weak pointers to the type itself.
package main
import (
"fmt"
"reflect"
"runtime"
"weak"
)
var instances = make(map[string]weak.Pointer[reflect.Value], 0)
// ExampleFunc creates a weak pointer for all pointer types in the struct and saves them to the global variable instances.
// This function is called by others in a library, and we cannot know the type of T in advance.
func ExampleFunc[T any](val T) {
rfType := reflect.TypeOf(val)
rfValue := reflect.ValueOf(val)
if rfType.Kind() != reflect.Struct {
panic("T must be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Field(i)
valueField := rfValue.Field(i)
if typeField.Type.Kind() == reflect.Pointer {
instances[typeField.Name] = weak.Make(&valueField)
}
}
}
type Struct1 struct {
Field1 *int
Field2 *string
}
func main() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
*s1.Field1 = 5
*s1.Field2 = "test"
ExampleFunc(s1)
runtime.GC()
for n, v := range instances {
fmt.Println(n, v.Value())
if v.Value() == nil {
panic(fmt.Sprintf("%v should not be nil", n))
}
}
runtime.KeepAlive(s1)
*s1.Field1 = 10
}
Now you are making it even more confusing. I just fixed the code you’d sent. Coming back to your first desire to store type and value from reflection you need to create your own container for this purpose. Create weak pointer to it. And do your own wrappers how to process its kind and value. But the biggest issue here, IMHO that you clearly do not understand well how generics work.