With the introduction of weak pointers in Go 1.24, I’ve been exploring their use circumstances. I’ve discovered a state of affairs the place I have to create and handle weak pointers dynamically, primarily based on sorts decided at runtime – primarily, I would like reflection capabilities for weak pointers.
The present weak.Pointer is generic, and so far as I can inform, Go doesn’t at present help calling generic capabilities through reflection. This makes it unimaginable to create a weak.Pointer[T] when T is barely generally known as a mirror.Sort.
I’ve appeared via the difficulty tracker and the golang-nuts mailing listing, and I haven’t discovered any current discussions instantly addressing this. Earlier than I submit a proper proposal on GitHub, I wished to gauge the group’s opinion on whether or not including reflection help for weak pointers can be a precious addition to the language.
Right here’s a tough sketch of what I envision the API would possibly seem like (utilizing a hypothetical weak.PointerAny):
bundle weak // or maybe a brand new bundle like mirror/weak
sort PointerAny struct {
typ mirror.Sort
u unsafe.Pointer
}
func MakeAny(ptr any) PointerAny {
ptr = abi.Escape(ptr)
v := mirror.ValueOf(ptr)
t := v.Sort()
if t.Form() != mirror.Pointer {
panic("ptr just isn't 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) Worth() any {
if p.u == nil {
return nil
}
ptr := runtime_makeStrongFromWeak(p.u)
if ptr == nil {
return nil
}
return mirror.NewAt(p.typ, ptr).Interface()
}
This PointerAny would enable making a weak pointer from an any (which have to be a pointer) and retrieving the unique worth as an any.
Is there a compelling cause to not add this? Would including reflection help for weak pointers introduce important complexity or have unintended penalties? Maybe there are basic explanation why this hasn’t been carried out already.
I’m keen to listen to your ideas. Thanks!
IMHO, weak pointer doesn’t have to know the precise sort of the worth. Replicate is at all times very expensive and I doubt it’s a standard use. You’ll be able to at all times merely add your individual wrapper round weak pointer with its sort:
sort MyPointer struct {
t mirror.Sort
p weak.Pointer
}
That wrapper doesn’t resolve the generic sort challenge for reflection. I nonetheless get the can't use generic sort weak.Pointer[T any] with out instantiation
error.
Yeah, as a result of reflection
and generics
function in numerous fields. One is for comptime one other is for runtime. You can even attempt to create pointer to any worth and go it to weak pointer creation, one thing like:
func Pointer[T any](val T) *T {
v := mirror.ValueOf(val)
if !v.CanAddr() {
v = mirror.ValueOf(&val)
}
ptr, okay := v.Interface().(*T)
if !okay {
panic("")
}
return ptr
}
I have to name a Make generic perform to create weak pointers once I solely have a mirror.Worth, which isn’t attainable with the present code. It would look one thing like this:
func MakeAny(ptr any) PointerAny
For those who imply bypassing the kind like this, it’s going to trigger an inside error.
func MakeAny(ptr any) weak.Pointer[struct{}] {
return weak.Make((*struct{})(mirror.ValueOf(ptr).UnsafePointer()))
}
You want a worth to be a pointer to deduce its sort. And I meant one thing like this:
bundle most important
import (
"fmt"
"mirror"
"weak"
)
func Pointer[T any](val T) *T {
v := mirror.ValueOf(val)
if !v.CanAddr() {
v = mirror.ValueOf(&val)
}
ptr, okay := v.Interface().(*T)
if !okay {
panic("")
}
return ptr
}
func most important() {
i := 5
p := weak.Make(Pointer(i))
fmt.Println(*p.Worth()) // Output: 5
}
I would like to make use of weak pointers on this state of affairs.
bundle most important
import (
"fmt"
"mirror"
)
sort WeaKPointer struct{}
func MakeAny(ptr any) WeaKPointer {
panic("not carried out")
}
func (p WeaKPointer) Worth() any {
panic("not carried out")
}
var cases = make(map[string]WeaKPointer)
// ExampleFunc creates a weak pointer for all pointer sorts within the struct and saves them to the worldwide variable cases.
// This perform known as by others in a library, and we can't know the kind of T prematurely.
func ExampleFunc[T any](val T) {
rfType := mirror.TypeOf(val)
rfValue := mirror.ValueOf(val)
if rfType.Form() != mirror.Struct {
panic("T have to be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Discipline(i)
valueField := rfValue.Discipline(i)
if typeField.Sort.Form() == mirror.Pointer {
cases[typeField.Name] = MakeAny(valueField.Interface())
}
}
}
sort Struct1 struct {
Field1 *int
Field2 *string
}
func most important() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
ExampleFunc(s1)
fmt.Println(cases)
}
bundle most important
import (
"fmt"
"mirror"
"weak"
)
var cases = make(map[string]weak.Pointer[reflect.Value], 0)
// ExampleFunc creates a weak pointer for all pointer sorts within the struct and saves them to the worldwide variable cases.
// This perform known as by others in a library, and we can't know the kind of T prematurely.
func ExampleFunc[T any](val T) {
rfType := mirror.TypeOf(val)
rfValue := mirror.ValueOf(val)
if rfType.Form() != mirror.Struct {
panic("T have to be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Discipline(i)
valueField := rfValue.Discipline(i)
if typeField.Sort.Form() == mirror.Pointer {
cases[typeField.Name] = weak.Make(&valueField)
}
}
}
sort Struct1 struct {
Field1 *int
Field2 *string
}
func most important() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
*s1.Field1 = 5
*s1.Field2 = "check"
ExampleFunc(s1)
for n, v := vary cases {
fmt.Println(n, v.Worth())
}
}
Right here, Make solely creates weak tips to mirror.Worth, not weak tips to the kind itself.
bundle most important
import (
"fmt"
"mirror"
"runtime"
"weak"
)
var cases = make(map[string]weak.Pointer[reflect.Value], 0)
// ExampleFunc creates a weak pointer for all pointer sorts within the struct and saves them to the worldwide variable cases.
// This perform known as by others in a library, and we can't know the kind of T prematurely.
func ExampleFunc[T any](val T) {
rfType := mirror.TypeOf(val)
rfValue := mirror.ValueOf(val)
if rfType.Form() != mirror.Struct {
panic("T have to be a struct")
}
for i := 0; i < rfType.NumField(); i++ {
typeField := rfType.Discipline(i)
valueField := rfValue.Discipline(i)
if typeField.Sort.Form() == mirror.Pointer {
cases[typeField.Name] = weak.Make(&valueField)
}
}
}
sort Struct1 struct {
Field1 *int
Field2 *string
}
func most important() {
s1 := Struct1{
Field1: new(int),
Field2: new(string),
}
*s1.Field1 = 5
*s1.Field2 = "check"
ExampleFunc(s1)
runtime.GC()
for n, v := vary cases {
fmt.Println(n, v.Worth())
if v.Worth() == nil {
panic(fmt.Sprintf("%v shouldn't be nil", n))
}
}
runtime.KeepAlive(s1)
*s1.Field1 = 10
}