You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I propose that the zero value of a unique.Handle[T] be explicitly defined as being a globally unique identity for the zero value of T. Specifically, I propose that:
unique.Make[T](T{}) should return the zero value of unique.Handle[T]
unique.Make[T]{}.Value() should return the zero value of T instead of panicking
This would have the effect of defining away the potential "invalid" state of a nil interior pointer.
Failing that, I propose that the documentation be updated to indicate that a zero value Handle is distinct from a Handle of the zero value, and clarify that it is potentially unsafe to use.
Motivation
The unique.Handle type is defined to be "a globally unique identity for some value of type T" and states that "Two handles compare equal exactly if the two values used to create the handles would have also compared equal." However, unique.Handle[T] actually has one more possible value than does T because it is possible for unique.Handle to be uninitialized.
This can create trouble when trying to intern a value of type T for types T with a useful zero value because it creates the potential for two distinct "zero" values: the zero value Handle[T] and the zero value T. At best, it is surprising to begin with a type T with a non-nil zero value and inadvertently introduce a hidden pointer after switching to unique.Handle[T]; at worst, it makes Value() potentially unsafe since it dereferences a hidden pointer. Issue #73266 makes reference to these difficulties.
Is there a strong use case for separating the two zero values that I am not aware of that justifies the sharp edge and makes it important that they be distinguishable?
Backwards Compatibility
I'm unsure whether this is a legal backwards-compatible change.
On one hand, the documentation of unique.Handle makes no mention of the behavior of its zero value. It also does not divulge that it has an interior pointer that can be nil and makes no reference to or commitments about invalid handles.
On the other hand, this would result in an observable behavioral change in programs. Consider:
invalid:= unique.Handle[int]{}
zero:=unique.Make(int(0))
fmt.Println(invalid==zero)
// Outputs "false" today, would change to "true" with this proposal
There is at least some precedent for making such potentially-observable behavior changes by leveraging the Forward Compatibility mechanism (#60078). I'm not familiar enough with it to know if that's an option here.
The text was updated successfully, but these errors were encountered:
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
tylerchr
changed the title
proposal: unique: Define Handle zero value as zero value of underlying type
proposal: unique: Define Handle[T] zero value as zero value of T
Apr 12, 2025
tylerchr
changed the title
proposal: unique: Define Handle[T] zero value as zero value of T
proposal: unique: Define Handle[T] zero value as handle to zero value of T
Apr 12, 2025
seankhliao
changed the title
proposal: unique: Define Handle[T] zero value as handle to zero value of T
proposal: unique: define Handle[T] zero value as handle to zero value of T
Apr 14, 2025
Proposal Details
I propose that the zero value of a
unique.Handle[T]
be explicitly defined as being a globally unique identity for the zero value ofT
. Specifically, I propose that:unique.Make[T](T{})
should return the zero value ofunique.Handle[T]
unique.Make[T]{}.Value()
should return the zero value ofT
instead of panickingThis would have the effect of defining away the potential "invalid" state of a nil interior pointer.
Failing that, I propose that the documentation be updated to indicate that a zero value
Handle
is distinct from aHandle
of the zero value, and clarify that it is potentially unsafe to use.Motivation
The
unique.Handle
type is defined to be "a globally unique identity for some value of type T" and states that "Two handles compare equal exactly if the two values used to create the handles would have also compared equal." However,unique.Handle[T]
actually has one more possible value than doesT
because it is possible forunique.Handle
to be uninitialized.This can create trouble when trying to intern a value of type
T
for typesT
with a useful zero value because it creates the potential for two distinct "zero" values: the zero valueHandle[T]
and the zero valueT
. At best, it is surprising to begin with a typeT
with a non-nil zero value and inadvertently introduce a hidden pointer after switching tounique.Handle[T]
; at worst, it makesValue()
potentially unsafe since it dereferences a hidden pointer. Issue #73266 makes reference to these difficulties.Is there a strong use case for separating the two zero values that I am not aware of that justifies the sharp edge and makes it important that they be distinguishable?
Backwards Compatibility
I'm unsure whether this is a legal backwards-compatible change.
On one hand, the documentation of
unique.Handle
makes no mention of the behavior of its zero value. It also does not divulge that it has an interior pointer that can benil
and makes no reference to or commitments about invalid handles.On the other hand, this would result in an observable behavioral change in programs. Consider:
There is at least some precedent for making such potentially-observable behavior changes by leveraging the Forward Compatibility mechanism (#60078). I'm not familiar enough with it to know if that's an option here.
The text was updated successfully, but these errors were encountered: