Converting a variable to a single-element slice in Go via unsafe
I was recently reading Chris Wellons' Go Slices are Fat Pointers. At the end of the article, Wellons says:
Slices aren’t as universal as pointers, at least at the moment. You can take the address of any variable using
&
, but you can’t take a slice of any variable, even if it would be logically sound.[...] However, if you really wanted to do this, the unsafe package can accomplish it. I believe the resulting slice would be perfectly safe to use:
// Convert to one-element array, then slice fooslice = (*[1]int)(unsafe.Pointer(&foo))[:]
I had to read this carefully before I understood what it was doing,
but then after I read the documentation for unsafe.Pointer()
carefully, I believe that
this is fully safe. So let's start with what it's doing. The
important thing is this portion of the expression:
(*[1]int)(unsafe.Pointer(&foo))
This is essentially reinterpreting foo
from an integer to a
one-element array of integers, by taking a pointer to it and then
converting that to a pointer to a one-element array. I believe
that this use of unsafe.Pointer()
is probably valid, because it
seems like it falls under the first valid use in the documentation:
(1) Conversion of a *T1 to Pointer to *T2.
Provided that T2 is no larger than T1 and that the two share an equivalent memory layout, this conversion allows reinterpreting data of one type as data of another type. [...]
In Go today, an integer and a one-element array of integers are the
same size, making the first clause true and pretty much requiring
that the second one is true as well. I don't think that Go requires
this in the language specification,
but in practice it's very likely to be the case in any implementation
that wants to adhere to Go's ethos of efficiency and minimalism.
Once we have a valid pointer to a (valid) one-element array of
int
, it's perfectly legal to create a slice from it, which is
what the '[:]
' does. So if this use of unsafe
is valid, the
resulting slice is fully safe and valid.
Now we get to the interesting question of why Go doesn't allow this
without the use of unsafe.Pointer()
. One possible answer is that
this is not allowed simply because it would require extra work in
the language specification and the compiler. This may well be the
case (and it's certainly a very Go style reason), but another
possibly reason is that Go doesn't want to require that all
implementations make a one-element array have exactly the same
memory layout and implementation as a single variable. By confining
this to the limited assurances of unsafe
and not making it part of the
guaranteed language specification, Go keeps people's options open.
(Of course this is only theoretical, because in practice a new
implementation will likely want to reuse as much of the standard
library as possible and the current standard library uses unsafe
in various places. If you don't match what works with unsafe
today
in mainline Go, you're going to have to rewrite some of that code.
Also, see how unsafe type conversions are still garbage collection
safe for some more discussion of this area.)
Comments on this page:
|
|