-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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 TypeFor #60088
Comments
Compare #50741 |
Gob uses tricks like
and this would let us write
which is definitely nicer. Static is probably not the right word - that's a C word with lots of overloaded meanings. But something with that behavior would help. |
I've wanted something like this on more than one occasion as well. There have been several talks about adding a nicer generic way to get a zero value of a type, something similar to |
Can someone do a corpus analysis to see what the relative frequencies of different reflect operations are (GenericTypeOf vs StaticTypeOf equivalents)? |
To bikeshed on shorter names: |
or even |
A cursory look at just the standard library reveals dozens of places where |
|
@ianlancetaylor I'm not sure I agree; I would personally want to use the generic API consistently. There would be little reason to use a mix of both APIs if the new one is more than enough. Consistency is the same reason why I use Put another way, I much prefer |
Thanks, that kind of makes this an instance of #48287. It's hard to think of a good name, since the right name is already taken. |
I feel a little confused by the discussion above because the proposal I see at the time of writing does not take a value and return its type, but instead takes a type directly (via a type parameter) and returns the reflect equivalent of that type. Has the proposal changed since the discussion started? I suppose the assumption of the subsequent discussion is that it's idiomatic to use the zero value of a type with However, I think even then this proposal is defensible, because directly asking for the reflect representation of a type is a clearer representation of author intent than asking for the type of the zero value of a type: reflect.TypeOf(0)
reflect.StaticTypeOf[int]() Of course this is subjective, but the second one reads more clearly to me as "get the reflect representation of In some codebases I'm responsible for I can see expressions very similar to the ones found in the standard library which would benefit from this proposal, like this one: // (intAlias is a named type based on int)
reflect.TypeOf((*intAlias)(nil)).Elem()
// (I don't recall why this one uses a pointer to a separate zero-valued variable rather
// than a nil pointer, but perhaps this just demonstrates that it's non-obvious how
// to get a reflect.Type of an interface type, as the proposal claims.)
var victimExpr hcl.Expression // hcl.Expression is an interface type
var exprType = reflect.TypeOf(&victimExpr).Elem() I can also see some examples like the one I showed above where the zero value to // this is from a test of an encoding/json-like marshalling library, testing the string case
reflect.TypeOf("")
// (cty.NilType is a variable containing the zero value of a struct type,
// so this is the same as writing an empty composite literal for that type.)
reflect.TypeOf(cty.NilType) The amount of variation in these examples suggests to me that having one obvious way to do it would also reduce the deviation caused by different developers inventing slightly different approaches to the same problem. I do agree that the name I feel okay about I don't see so much value in a generic version of |
It's a little odd, but perhaps |
I think I'd be happy with |
This proposal has been added to the active column of the proposals project |
I love this proposal, but the "make" in I know nobody loves the word "Get" but @apparentlymart's |
Yet another color is |
If we're listing all possible names, #60274 suggests the name |
Maybe |
|
|
Maybe |
|
Change https://go.dev/cl/514639 mentions this issue: |
Change https://go.dev/cl/514640 mentions this issue: |
This avoids several mildly confusing Elem calls. For #60088 Change-Id: If7b83d2ab10537c7e886a035b43cb272130c1669 Reviewed-on: https://go-review.googlesource.com/c/go/+/514455 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Rob Pike <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
For #60088 Change-Id: Ib2589b994d304cca1f2e2081639959d80818ac7f Reviewed-on: https://go-review.googlesource.com/c/go/+/514639 Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: David Chase <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Joseph Tsai <[email protected]>
For #60088 Change-Id: I2e471c76de62944b14472966b63f5778124b9b8b Reviewed-on: https://go-review.googlesource.com/c/go/+/514655 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Joseph Tsai <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Joseph Tsai <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Daniel Martí <[email protected]>
For #60088 Change-Id: Ibc3983ca5cfe396087ddfa96c43cfe32ca47129a Reviewed-on: https://go-review.googlesource.com/c/go/+/514640 Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Daniel Martí <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Rob Pike <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]>
Change https://go.dev/cl/514975 mentions this issue: |
Change https://go.dev/cl/514995 mentions this issue: |
Change https://go.dev/cl/515175 mentions this issue: |
For #60088 Change-Id: I56586b68d5e38a46560f4ced19214f1d2db2850e Reviewed-on: https://go-review.googlesource.com/c/go/+/514995 Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Rob Pike <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
For #60088 Change-Id: Id19435e864bcfd2adbb1492db3f8cdf2ee3c915e Reviewed-on: https://go-review.googlesource.com/c/go/+/515175 Auto-Submit: Ian Lance Taylor <[email protected]> Run-TryBot: shuang cui <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
For #60088 Change-Id: Ib05ba3d456b22f7370459037b3d263c4b3ebe3b1 Reviewed-on: https://go-review.googlesource.com/c/go/+/514975 Reviewed-by: Daniel Theophanes <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
Change https://go.dev/cl/522338 mentions this issue: |
For #60088 Change-Id: I9e4044d9c2694fe86aab1f5220622c8d952b1a90 Reviewed-on: https://go-review.googlesource.com/c/go/+/522338 Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Damien Neil <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
Change https://go.dev/cl/524259 mentions this issue: |
For #60088 Change-Id: I4b2a5c6c59ef26361f343052a4ddaabde5d3bc94 GitHub-Last-Rev: d519835 GitHub-Pull-Request: #62370 Reviewed-on: https://go-review.googlesource.com/c/go/+/524259 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
Change https://go.dev/cl/555597 mentions this issue: |
The reflect.Type.Elem method is somewhat slow, which is unfortunate since the reflect.TypeOf((*T)(nil)).Elem() trick is only needed if T is an interface. Optimize for concrete types by doing the faster reflect.TypeOf(v) call first and only falling back on the Elem method if needed. Performance: name old time/op new time/op delta TypeForString-24 9.10ns ± 1% 1.78ns ± 2% -80.49% (p=0.000 n=10+10) TypeForError-24 9.55ns ± 1% 9.78ns ± 1% +2.39% (p=0.000 n=10+9) Updates #60088 Change-Id: I2ae76988c9a3dbcbae10d2c19b55db3c8d4559bf Reviewed-on: https://go-review.googlesource.com/c/go/+/555597 Auto-Submit: Joseph Tsai <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Run-TryBot: Joseph Tsai <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Mauri de Souza Meneguzzo <[email protected]>
The reflect.Type.Elem method is somewhat slow, which is unfortunate since the reflect.TypeOf((*T)(nil)).Elem() trick is only needed if T is an interface. Optimize for concrete types by doing the faster reflect.TypeOf(v) call first and only falling back on the Elem method if needed. Performance: name old time/op new time/op delta TypeForString-24 9.10ns ± 1% 1.78ns ± 2% -80.49% (p=0.000 n=10+10) TypeForError-24 9.55ns ± 1% 9.78ns ± 1% +2.39% (p=0.000 n=10+9) Updates golang#60088 Change-Id: I2ae76988c9a3dbcbae10d2c19b55db3c8d4559bf Reviewed-on: https://go-review.googlesource.com/c/go/+/555597 Auto-Submit: Joseph Tsai <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Run-TryBot: Joseph Tsai <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Mauri de Souza Meneguzzo <[email protected]>
hi @dsnet, is this optimization necessary?
type Type struct{ /** copy of abi.Type **/ }
type PtrType struct { Type; Elem *Type }
var abiTypeITab uintptr
func init() {
t := reflect.TypeOf(0)
abiTypeITab = (*[2]uintptr)(unsafe.Pointer(&t))[0]
}
func TypeFor2[T any]() (t reflect.Type) {
v := any((*T)(nil))
vp := (*[2]uintptr)(unsafe.Pointer(&v))
tp := (*[2]uintptr)(unsafe.Pointer(&t))
tp[0] = abiTypeITab
tp[1] = *(*uintptr)(unsafe.Pointer(vp[0] + unsafe.Offsetof(PtrType{}.Elem)))
return
} var sinkType reflect.Type
func BenchmarkTypeForString(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = TypeFor[string]()
}
}
func BenchmarkTypeForError(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = TypeFor[error]()
}
}
func BenchmarkTypeForString2(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = TypeFor2[string]()
}
}
func BenchmarkTypeForError2(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = TypeFor2[error]()
}
} |
This function can be inlined. func TypeFor2[T any]() (t reflect.Type) {
v := any((*T)(nil))
vp := (*[2]uintptr)(unsafe.Pointer(&v))
tp := (*[2]uintptr)(unsafe.Pointer(&t))
tp[0] = abiTypeITab
tp[1] = *(*uintptr)(unsafe.Pointer(vp[0] + unsafe.Offsetof(PtrType{}.Elem)))
return
} |
Rather than do this kind of inscrutable unsafe data structure manipulation, let's improve the compiler. Thanks. |
reflect.TypeOf
is one of a few fundamental reflect APIs. It returns the dynamic type of a value. This is, strictly speaking, sufficient. However, it is non-obvious how to use it to get a static type. The correct code is long, awkward, and hard to explain.Pre-generics, there was no way to write a nice API to get a static reflect.Type. Now there is.
I propose that we add it.
It is a single line implementation, but it fills an obvious gap in the package reflect API and makes call sites clear.
I strongly suspect that almost all uses would be in conjunction with
reflect.Type.Implements
. An alternative would be make a generic version of implements that accepts an interface type as a type parameter, but fundamental building blocks compose better.EDIT: this originally proposed
StaticTypeOf
; renamed toMakeType
, per discussion.The text was updated successfully, but these errors were encountered: