Wandering Thoughts archives


Go 1.17 will allow converting a slice to an array pointer (some of the time)

In Go, slices contain a reference to their backing array, whether this is an array that exists somewhere as a distinct variable of its own or is simply an anonymous array that was allocated to support the slice (that it can be either case can sometime make slices seem like two data structures in one). The presence of this backing array can lead to interesting memory leaks or just surprising changes to your slices, but in Go through 1.16 you can't get direct access to the backing array without using the reflect and unsafe packages. However, it's possible to do this safely in some cases, and there's been a long standing Go enhancement request that it be possible in issue #395 (which dates from 2009, shortly after Go was announced and well before Go 1.0 was released).

In Go 1.17 this will be possible, due to a series of changes starting with commit 1c268431f4, which updates the specification. The specification's description of this is straightforward:

Converting a slice to an array pointer yields a pointer to the underlying array of the slice. If the length of the slice is less than the length of the array, a run-time panic occurs.

It's harmless for the slice to be longer than the length of the array (although, as usual, this will keep the entire backing array of the slice alive). A longer slice than the array only means that your array won't be able to access all of the original slice's backing array.

The specification has some examples (all comments are from the specification):

s := make([]byte, 2, 4)
s0 := (*[0]byte)(s)      // s0 != nil
s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

var t []string
t0 := (*[0]string)(t)    // t0 == nil
t1 := (*[1]string)(t)    // panics: len([1]string) > len(s)

The discussion around s2 shows that this conversion doesn't (and can't) allocate a new array, making it guaranteed to be efficient. The cases of s0 and t0 are interesting; converting a non-empty slice to a 0-length array must give you a valid pointer, even though you can't do anything with it, but converting an empty slice gives you a nil.

At the moment there's no way to do this conversion with a check to see if it would panic, the way you can with type assertions. If you think you might have a too-short slice, you need to use an if.

The reflect package has also been updated to support conversions from slices to array pointers, in commit 760d3b2a16. If you're working with reflect for this, you should read the caution from the commit:

Note that this removes an invariant:

v.Type().ConvertibleTo(t) might return true, yet v.Convert(t) might panic nevertheless.

This is a fairly unavoidable consequence of the decision to add the first-ever conversion that can panic.

ConvertibleTo describes a relationship between types, but whether the conversion panics now depends on the value, not just the type.

(I believe that you can always use reflect.ArrayOf() to create a type with the right number of elements, and then reflect.PtrTo() to create the necessary 'pointer to array of size X' type.)

programming/GoConvertSliceToArray written at 23:46:14; Add Comment

Page tools: See As Normal.
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.
