-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
reflect: add TypeAssert #62121
Comments
#57060 is a compiler optimization that may obviate the need for this API as this falls under the "short-lived heap allocated value" problem. |
Empirical evidence based on module proxy data on 2023-07-01:
This means at least ~40% usages of the |
As a performance workaround, you could do the following today: func AssertTo[T any](rv reflect.Value) (v T, ok bool) {
if v.CanAddr() {
v, ok = *v.Addr().Interface().(*T)
} else {
v, ok = v.Interface().(T)
}
return v, ok
} It's gross, but works. |
Bikeshed: |
I like |
@ianlancetaylor could I gently nudge this proposal for consideration? It would very slightly help with the encoding/json/v2 work :) |
This proposal has been added to the active column of the proposals project |
Plain 'Assert' might be confusing with C-style asserts. Should we call it TypeAssert? With the Type prefix we may not need the To suffix either. t, ok := reflect.TypeAssert[time.Time](v) I think this reads better than TypeAssertTo. |
Have all remaining concerns about this proposal been addressed? The proposal is to add to package reflect:
|
Based on the discussion above, this proposal seems like a likely accept. The proposal is to add to package reflect:
|
How about also adding
So that sometimes, we can use
instead of
Both of the two functions have their own ideal use cases. |
Let's start with just one. |
No change in consensus, so accepted. 🎉 The proposal is to add to package reflect:
|
Change https://go.dev/cl/591495 mentions this issue: |
The CL for this got abandoned. @dsnet did you want to get it in before the freeze? |
Change https://go.dev/cl/648056 mentions this issue: |
While looking at the diff i realized that, // TypeAssert is semantically equivalent to:
// v2, ok := v.Interface().(T)
func TypeAssert[T any](v reflect.Value) (T, bool) Do we really want that? Consider: func TestTypeAssert2(t *testing.T) {
val := ValueOf(&net.DNSError{})
_, ok := TypeAssert[error](val)
t.Log(ok) // false
_, ok = val.Interface().(error)
t.Log(ok) // true
} The implementation in CL 648056 returns |
The goal is for the reflect package to more or less duplicate the language. Since the language permits type assertions to interface types, I would expect Is there a good reason that we shouldn't implement the language behavior? |
@mateusz834, no worries. I had a local CL for this that I was gonna send out after the freeze. I don't mind going with your CL so long as it's fast for concrete types and I get to use it in Go 1.25 😄.
When I implemented this myself, I noticed this difference as well. Implementation-wise, it seems easier to violate the documented behavior, but on the other hand, it's very easy to explain My approach (CL/648235) only special cases concrete types and delegates the handling of interfaces to |
Change https://go.dev/cl/648235 mentions this issue: |
There is also this kind of behavior in the func newPtr[T any](t T) *T {
return &t
}
func TestTypeAssert2(t *testing.T) {
val := reflect.ValueOf(newPtr(any(1))).Elem()
_, ok := val.Interface().(int)
t.Log(ok) // true
val = reflect.ValueOf(newPtr(error(&net.DNSError{}))).Elem()
_, ok = val.Interface().(*net.DNSError)
t.Log(ok) // true
} Lines 1498 to 1508 in 7c1a413
I don't know whether EDIT: It makes sense in terms of |
Consider the following benchmark:
This currently prints:
Since the underlying
time.Time
is addressable, theInterface
method must make a copy of it.However, one primary reason to use the
Interface
method is to then assert it to a particular concrete type.I propose the addition of:
which avoids the allocation since the value is never boxed in an interface.
Usage would then look like:
and naturally matches type-assertion construct in the Go language itself.
Ideally, this would be a method on
Value
, but we don't have #49085.If we did, the signature would be:
and usage would be more natural as:
The text was updated successfully, but these errors were encountered: