Description
To go along with the already accepted #395, I would love to see support for converting/aliasing a pointer of some type, to a single-element array pointer of the same type.
The main use-case would be passing an already-allocated single item to a function that accepts a slice of those items, avoiding an extra allocation and copy.
Another place this might be useful is in the reflect package, which mentions issue #2320.
Edit: Below are my answers to the questions from the Go 2 language change template.
Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced.
What other languages do you have experience with?
None quite as much as Go at this point, but (to varying degrees): Rust, Zig, Ruby, C, Erlang, and Haskell.
Would this change make Go easier or harder to learn, and why?
It wouldn't make it easier, but I don't think it would make it harder either, unless you count learning obscure features of a language.
Has this idea, or one like it, been proposed before? If so, how does this proposal differ?
I'm not aware of any proposal exactly like this, but there are some related (and already accepted) proposals. Primarily #395, and to some extent #19367. The former was the inspiration for this proposal, and the latter would allow this to be done with less-nasty unsafe
code than before.
The difference here is that Go would allow converting between these pointer types without unsafe
, since there shouldn't really be anything unsafe about it.
Who does this proposal help, and why?
This proposal helps in performance-sensitive code where allocations are undesirable. When you already have a pointer to a heap-allocated value, and you need a slice (because a function signature demands it), you can avoid allocating space for a single-element slice and copying the value to it.
The only way to do this now is by using unsafe. Alternatively, you could work with a *[1]T
instead of a *T
from the beginning, but that isn't very ergonomic, and wouldn't be a viable option in the public interface of a library.
What is the proposed change?
The proposal is to allow unsafe-free conversion (or aliasing) of a pointer of some type, to an array pointer with a single element of the same type. You could then slice that array pointer.
Alternatively, we could allow converting to a slice directly (since *[1]T
is rarely useful by itself), but that seems less general.
Please describe as precisely as possible the change to the language.
I'm not sure how to answer this any differently from my answer to the previous question.
What would change in the language spec?
Probably just adding a new item in the Conversions section where says:
A non-constant value x can be converted to type T in any of these cases.
Please also describe the change informally, as in a class teaching Go.
Yikes, I'll have to think about this one a bit.
Is this change backward compatible?
Yes. All code that was valid before will remain valid. Some code that would have required unsafe
before will no longer require it.
Show example code before and after the change.
Before:
func ExamplePointer(ptr *SomeType) {
ExampleSlice([]SomeType{*ptr})
}
func ExampleSlice(slice []SomeType) {
for i := range slice {
// ... do something with &slice[i]
}
}
After:
func ExamplePointer(ptr *SomeType) {
ExampleSlice((*[1]SomeType)(ptr)[:])
}
func ExampleSlice(slice []SomeType) {
// Same as before.
}
Yes, it's longer and uglier, but also doesn't allocate.
What is the cost of this proposal? (Every language change has a cost).
I don't really know. I haven't implemented a language change and I'm not very familiar with the code that would need to be changed in the compiler. The documentation cost should be relatively small.
How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
I think the compiler would have given an error before this change, and thus vet
would not likely need any logical changes. Beyond that, I'm not really sure.
What is the compile time cost?
It should be negligible.
What is the run time cost?
Where it is used, it can speed up execution slightly by avoiding unnecessary allocations. Otherwise, it has no impact.
Can you describe a possible implementation?
Nope. I'm just not familiar enough with the compiler.
Do you have a prototype? (This is not required.)
No, but you can achieve this with some rather nasty unsafe right now.
How would the language spec change?
I think I already answered this question above.
Orthogonality: how does this change interact or overlap with existing features?
The change would overlap somewhat with #19367 (accepted proposal, not exactly existing), except that it wouldn't require unsafe
.
Is the goal of this change a performance improvement?
Yes. Strictly performance.
If so, what quantifiable improvement should we expect?
Fewer allocations and copying. It would be more noticeable if T
is large.
How would we measure it?
A go test
micro-benchmark.
Does this affect error handling?
No.
If so, how does this differ from previous error handling proposals?
Is this about generics?
No.
If so, how does this differ from the the current design draft and the previous generics proposals?