-
Notifications
You must be signed in to change notification settings - Fork 18k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # 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: