This is an outgrowth of #32779, and also of https://mdlayher.com/blog/unsafe-string-interning-in-go/ and ensuing discussion of this on Gopher Slack.
Occasionally, it would be desireable to have weak references. Unfortunately, you can't implement these as a user; you need hooks into the runtime to make a safe weak reference implementation, I think. The string interning hack described above probably works but is not complying with the uintptr conversion rules.
In nearly every case where weak references are desireable, it's desireable to be able to look up specific weak references, which suggests something similar to a map data type. It's also desireable to be able to find out whether the weak reference exists, or to be able to request the reference, getting an actual stable non-weak reference back, without a separation between the query and the retrieval.
Conveniently, a map with a pointer key type already provides exactly this behavior. What it doesn't provide is the potential for GC to remove objects.
Thus, my proposal:
cache := make(weak map[string]*Data)
This is equivalent to make(map[string]*Data), except that things which are in the map but not referenced anywhere else may become deleted from the map at any arbitrary time. However, retrieval from the map is guaranteed to either yield a nil (and a false in the ,ok form) or the pointer last stored for that key, and if it yields a non-nil pointer, that pointer is safe to use.
The internal implementation details aren't 100% obvious to me. You could nearly do it with SetFinalizer, except you need a way to ensure that this doesn't clash with any other finalizers used on the item. (For instance, the same value could be present in two weak maps...)
I'm not totally sure about the syntax, but I'm pretty sure that this wouldn't compile now under any possible circumstances, so it doesn't break any existing code.
Why not sync.WeakMap, parallel to sync.Map and/or sync.Pool? Because type safety is nice, and because the ability to use or not use the ", ok" form of retrieval seems desireable. Plain old maps are easier and friendlier to use, and less error-prone.
That said, sync.WeakMap, which would vaguely combine the behaviors of sync.Map and sync.Pool, would be viable too. The main issue this is intended to address is that "would like to keep this if it's convenient and/or if it's being used, but don't need to retain it if there's memory pressure" seems like a common use case.
This is an outgrowth of #32779, and also of https://mdlayher.com/blog/unsafe-string-interning-in-go/ and ensuing discussion of this on Gopher Slack.
Occasionally, it would be desireable to have weak references. Unfortunately, you can't implement these as a user; you need hooks into the runtime to make a safe weak reference implementation, I think. The string interning hack described above probably works but is not complying with the uintptr conversion rules.
In nearly every case where weak references are desireable, it's desireable to be able to look up specific weak references, which suggests something similar to a map data type. It's also desireable to be able to find out whether the weak reference exists, or to be able to request the reference, getting an actual stable non-weak reference back, without a separation between the query and the retrieval.
Conveniently, a map with a pointer key type already provides exactly this behavior. What it doesn't provide is the potential for GC to remove objects.
Thus, my proposal:
cache := make(weak map[string]*Data)This is equivalent to
make(map[string]*Data), except that things which are in the map but not referenced anywhere else may become deleted from the map at any arbitrary time. However, retrieval from the map is guaranteed to either yield a nil (and a false in the ,ok form) or the pointer last stored for that key, and if it yields a non-nil pointer, that pointer is safe to use.The internal implementation details aren't 100% obvious to me. You could nearly do it with SetFinalizer, except you need a way to ensure that this doesn't clash with any other finalizers used on the item. (For instance, the same value could be present in two weak maps...)
I'm not totally sure about the syntax, but I'm pretty sure that this wouldn't compile now under any possible circumstances, so it doesn't break any existing code.
Why not
sync.WeakMap, parallel to sync.Map and/or sync.Pool? Because type safety is nice, and because the ability to use or not use the ", ok" form of retrieval seems desireable. Plain old maps are easier and friendlier to use, and less error-prone.That said,
sync.WeakMap, which would vaguely combine the behaviors of sync.Map and sync.Pool, would be viable too. The main issue this is intended to address is that "would like to keep this if it's convenient and/or if it's being used, but don't need to retain it if there's memory pressure" seems like a common use case.