-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: reflect: reflect.ValueOf(nil).IsNil() panics; let's make it return true #51649
Comments
I think this would be more likely to cause breakage, as I've seen the uses of the zero and invalid
I think that's reasonable. If someone does want the invalid value, they can always do |
Here's a slightly better search for changing the zero value: Go files which do both |
Thinking outloud: can this be one of those instances where we change the behavior of a Go API based on the That kind of backwards compatibility might get a bit complex when |
FWIW I am skeptical that changing this would be helpful enough to mitigate the breakage it will cause. The behavior is clearly documented:
|
This proposal has been added to the active column of the proposals project |
I understand that it is documented; my point is that what is documented is far from intuitive. It was perhaps a mistake to make the zero value represent invalid and nil both. But I also understand it may be hard to change. I wonder, though, what would actually break if reflect.ValueOf(nil).IsNil() did not panic. |
package main
import "reflect"
func main() {
var v any
y := reflect.ValueOf(&v).Elem()
println(y.IsNil()) // true
} |
That was my thought too. I think it would probably be fine if:
In general a nil Type represents "no type" in the same way that the zero Value represents "no value", and nil is the nearest we've got in Go to a generic way to represent "nothing", so IsNil returning true seems reasonable to me. I wonder if there's any code at all that would break if this behaviour were to change. |
The big problem here is that reflect is about typed values yet in many use cases people want some representation for untyped nil. And reflect.ValueOf(nil) returns the zero reflect.Value, which people use as that representation. So suppose we make the following changes to support that (assume v is the zero reflect.Value):
It's a little inconsistent but it will fix some code and probably won't break much, since all these panic today and code is written to avoid those panics. Does anyone object to this? |
SGTM. Note that |
I'm worried about the fact that if people start using |
Maybe untyped nil could be a new Kind? |
I guess the next question would whether it's OK to write reflect.Value{}.Convert(reflect.TypeOf(make(map[int]string))) to get a v.Set(reflect.Value{}) if I also think it's worth noting that there is no way to describe an untyped integer in a |
Untyped nil can't really be a new Kind because lots of code assumes what reflect.NewValue(nil) will return - the zero Value. I agree that special-casing Convert and Set for zero Value representing nil would be the next logical extensions. It does give me some pause about heading down this road, especially since untyped integers don't have this kind of support. |
If we treat a zero Changes for cases where
Changes for cases where
|
Thanks for that list @dsnet. It still seems a bit odd to me that we would put all this work into untyped nil when we don't have a similar answer for untyped 1. But untyped nil does seem to bite people a lot more than nil. Does anyone want to prototype this? We could run it through our internal tests at Google to see how much might break. |
Let's put this on hold for a prototype that we can evaluate against real programs. |
Placed on hold. |
💯 I hit this again today. I'm really nervous that changing reflect behavior even in minor ways will break any manner of things. And almost by definition, code written using reflect is hard to debug, often very hard. (Even just diagnosing the existing problem here was hard.) Given that time travel is sadly not an option, perhaps part of the answer is to update the ValueOf docs to point people in the right direction: // ValueOf returns a new Value initialized to the concrete value stored in the interface i.
// ValueOf(nil) returns the zero Value.
// Use ValueOf(new(any)).Elem() to get a valid Value containing untyped nil. |
The IsNil method of reflect.Value applies only to typed nils, so this program panics:
On the face of it, this is nuts: it says that nil is not nil. (What is this, a NaN? Just joking.) In fact, even asking if nil is nil causes the program to crash!
Every time I dig into uses of reflect, which would be rare except that I "own" several core packages that depend on reflection, I bounce off this. It just sits wrong with me that reflect.ValueOf(nil) gives the zero reflect.Value, but that is "invalid" not nil.
I propose, without thinking through the consequences in nearly enough detail, that either "invalid" becomes different from "value created from literal nil", or that we just change "invalid" to "nil" and allow it to satisfy IsNil.
I'm not sure this can be changed without causing a major ruckus, but I thought I'd at least ask. It might be easy and could clean up a fair bit of code in some places.
The text was updated successfully, but these errors were encountered: