-
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
spec: use (*[4]int)(x) to convert slice x into array pointer #395
Comments
I think this functionality would be nice. Personally I would rather not assume that the compiler can subtract arbitrary expressions (as in b := a.[0:4]) but instead say explicitly what type I want: b := (*[4]int)(a[0:4]) The argument against this is that we hoped introducing x[:] would let us get rid of the implicit conversion from *[4]int to slice. Maybe it still does, but we allow the explicit one. There are certainly compelling cases (mostly in low-level things like jpeg or sha1 block processing) where converting a slice to *[N]int for some N would eliminate many bounds checks for cheap. Owner changed to [email protected]. |
Yes, that's a disadvantage of (*[4]int)(a[0:4]). A disadvantage of a[0:4].(*[4]int) is that it takes over syntax currently reserved for interface values. At one point conversion syntax and type guard syntax was interchangeable. It clarified things quite a bit to require that in x.(T), x must be an interface value and that in T(x), the conversion must be statically guaranteed to succeed. Unfortunately, this particular conversion doesn't fit into either of those categories. We've got enough going on that's it going to be a while before we do anything with this. |
I just encountered a nice example of when this functionality might be useful. To quote from a reddit user on why Go "needs" pointer arithmetic: "One well-used example is making classes as small as possible for tree nodes or linked list nodes so you can cram as many of them into L1 cache lines as possible. This is done by each node having a single pointer to a left sub-node, and the right sub-node being accessed by the pointer to the left sub-node + 1. This saves the 8-bytes for the right-node pointer. To do this you have to pre-allocate all the nodes in a vector or array so they're laid out in memory sequentially, but it's worth it when you need it for performance. (This also has the added benefit of the prefetchers being able to help things along performance-wise - at least in the linked list case).'' You can *almost* do this in Go with type node struct { value int children *[2]node } except that there's no way of getting a *[2]node from the underlying slice. |
If neither syntax is ideal, perhaps a new unslice builtin? ary, ok := unslice([n]T, slc) Though should ary have type [n]T or *[n]T? If n is large and the unslice fails, a large zeroed array might not be ideal. Anything wrong with this that I'm not seeing? Well, besides it being another new builtin. |
If we were going to do it - which is far from even up for debate - I think the syntax x.(*[10]int) works well (x has type []int). You can't .(T) a slice type right now, so it would not be overloading anything, and like an interface type assertion it can fail or be checked at run time. You can even think of it as []int containing a *[10]int the same way an interface value holds a concrete type, and you're extracting the underlying array pointer. That said, I don't think this is important enough to worry about now. There is enough else going on. |
Since this is something that's been bugging me lately too, I thought I'd add a few random thoughts I had that don't seem to have been mentioned: Allowing conversions from slices to array-pointers means pointers can now refer to partially overlapping objects. I don't believe that's currently possible in the language. Slices can already overlap though, so it's not a big change overall. Like #8 says, []T is sort-of an interface type for *[N]T, so type assertions are arguably suitable syntax. Except that cap(x.(*[N]T)) might give a different value than cap(x), which isn't true for other interfaces/implementor-type relations. Seems like an open question whether this inconsistency is worth accepting into the language, and really since there's already a way to convert a *[N]T into a []T, just the ability to turn a []T into a *[N]T is the relevant missing feature. It would be nice if an expression like x[e1:e2] could actually have a static type of *[e2-e1]T (assuming e1 and e2 are constant expressions), then you could write something like *dst[16:24] = *src[136:144] and the compiler can verify that the array bounds match up. Unfortunately, the expression can't actually be x[e1:e2] since existing code might rely on cap(x[e1:e2]) == cap(x)-e1, and that would be a backwards incompatible change. The x.[e1:e2] syntax suggested originally would solve this issue. If you want a range like x.[n:n+4], instead of requiring the language to recognize this pattern somehow, x[n:].[:4] is equivalent and has static indices at the expense of clunkier notation. A short-hand notation like x.[n:+4] might be nice to indicate that 4 is a length not an end position, but not strictly necessary and complicates the language. (Also +4 here is technically ambiguous here whether it's length 4 or end position "+4", so again some new notation would be necessary.) |
I just want to note that we have a use case for this at go-gl. More information: go-gl/gl#111 The underlying OpenGL API only accepts the equivalent of, e.g, a *[4]float32, so it is nice to have this in the type system on our side. OTOH, a consumer of this API might be holding a []float32 they want to pass to us. So it would be great to find a solution to this, as the current solutions are a bit messy or require the use of unsafe. |
An alternative which occurred to me was in a function which only indexes a slice with constants values (eg the JPEG routines or unrolled FFTs which are what I'm working on) and the slice doesn't change the compiler could bounds check the slice just once at the start of the function with min(constants) and max(constants). This would achieve the removal of the bounds checking without a language change. It wouldn't allow the compiler to do range checking at compile time though. |
Another use case is to be able to use a small slice of known length as a map key. The Go API uses slices heavily even though the same length is expected. Being able to get the underlying array would allow usage as map keys. Two examples I've run into recently are hashes (SHA-256) and IP addresses (as 16 byte slices). It seems rather wasteful to have to copy them or transform them to strings to have to use them as map keys. |
FWIW, something similar to unslice() above can be implemented with reflect and unsafe. Despite being implemented in terms of unsafe, I believe unslice itself is safe. I don't know whether it violates any assumptions made by the GC, however. |
Actually I believe the compiler could easily be smart enough to make a single range check for statement like this:
|
Is there a good way to do this currently? For example if one function gives me a slice as output and I need to use that output in another function that wants a fixed array as input. What is the best way to coerce the slice into an array that is the current size of the slice and containing it's current members? |
Currently there is no safe way to convert from a slice type to an array type (that is the point of this issue). You can do it using unsafe by writing code like
|
These match the changes to cmd/compile/internal/types2 in CL 301650. Updates #395 Change-Id: I1e85b6355c8c8fdba0996c26a2505c65fab908d6 Reviewed-on: https://go-review.googlesource.com/c/go/+/301651 Trust: Josh Bleecher Snyder <[email protected]> Trust: Robert Griesemer <[email protected]> Run-TryBot: Josh Bleecher Snyder <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
Change https://golang.org/cl/312070 mentions this issue: |
Updates #395 Fixes #45665 Change-Id: Iaf053c0439a573e9193d40942fbdb22ac3b4d3bb Reviewed-on: https://go-review.googlesource.com/c/go/+/312070 Trust: Cuong Manh Le <[email protected]> Run-TryBot: Cuong Manh Le <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> TryBot-Result: Go Bot <[email protected]>
Change https://golang.org/cl/315169 mentions this issue: |
…es go1.17 Add missing version check. Even though this is a new types2 error we separate between the compiler and the types2 error message: we have the compiler error message to match the compiler style, and we have a types2-specific error message to match the types2 style for these kinds of errors (for now). Eventually we need to decide which style we like better and clean this up. Follow-up on https://golang.org/cl/301650. Updates #395. Change-Id: I5b779f345994c66b1f4a4db466466f98b7d3c491 Reviewed-on: https://go-review.googlesource.com/c/go/+/315169 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
Change https://golang.org/cl/325549 mentions this issue: |
Change https://golang.org/cl/327649 mentions this issue: |
CL 301650 adds conversion from slice to array ptr. The conversion expression may appear as argument to a function call, so it will be tested by mayCall. But ir.OSLICE2ARRPTR op is not handled by mayCall, causes the compiler crashes. Updates #395 Fixes #46720 Change-Id: I39e1b3e38e224a31f3dec46dbbdc855ff3b2c6a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/327649 Trust: Cuong Manh Le <[email protected]> Trust: Josh Bleecher Snyder <[email protected]> Run-TryBot: Cuong Manh Le <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> Reviewed-by: Josh Bleecher Snyder <[email protected]> TryBot-Result: Go Bot <[email protected]>
Change https://golang.org/cl/334669 mentions this issue: |
For #395 For #46746 Change-Id: I4bfc094cf1cecd27ce48e31f92384cf470f371a6 Reviewed-on: https://go-review.googlesource.com/c/go/+/334669 Trust: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Joe Tsai <[email protected]>
Change https://golang.org/cl/336890 mentions this issue: |
There is an example for nil slice already, so adding example for non-nil zero length slice, too, clarifying to the reader that the result is also non-nil and different from nil slice case. Updates #395 Change-Id: I019db1b1a1c0c621161ecaaacab5a4d888764b1a Reviewed-on: https://go-review.googlesource.com/c/go/+/336890 Trust: Cuong Manh Le <[email protected]> Trust: Robert Griesemer <[email protected]> Run-TryBot: Cuong Manh Le <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
Change https://golang.org/cl/338630 mentions this issue: |
Panic if the slice is too short. For golang/go#395 Change-Id: I184f87d0207dcee4be6b36ae446b84e9583b356d Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338630 Trust: Ian Lance Taylor <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
Panic if the slice is too short. For golang/go#395 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338630
Change https://golang.org/cl/339329 mentions this issue: |
When checking a slice to pointer-to-array conversion, I forgot to verify that the elements types are identical. For golang/go#395 Change-Id: I533ac52c0b390af96fce78a8c468ae9d8ad79da9 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339329 Trust: Ian Lance Taylor <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
When checking a slice to pointer-to-array conversion, I forgot to verify that the elements types are identical. For golang/go#395 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339329
Maybe it's a bit too late to comment since the feature is already landed on Go 1.17, but I just want to point out that the >= behavior might lead to a hidden bug due to a different behavior expected by many programmers, i.e. they expect the conversion will success only if the lengths are equal. It violates the principle of least surprise. The == behavior is also more explicit, which is usually preferred by the Go community, than the >= behavior: If the original slice's length is larger than the destination array's length, the slice needs to be first resized to the array's size. |
The text was updated successfully, but these errors were encountered: