-
Notifications
You must be signed in to change notification settings - Fork 18.9k
Description
What version of Go are you using (go version)?
$ go version 1.14
Does this issue reproduce with the latest release?
Sure.
What operating system and processor architecture are you using (go env)?
N/A.
What did you do?
var a [65536]byte
a64 := (*[16384]uint64)(unsafe.Pointer(&a))[:16384]
(In my defense, I was not quite awake yet.)
What did you expect to see?
Probably a bounds check error?
What did you see instead?
Nothing.
And yes, I understand: unsafe is what it says on the tin. But I think there's a bit of a flaw in the design space right now. There's no middle ground between "only completely strictly-supported conversions" and "no sanity-checks or error-checking of any kind".
A while back, I was confused about some of the SliceHeader docs, and filed an issue (#33794) about that, and Ian helpfully pointed out a canonical idiom:
asUint16 := (*[1<<29]uint16)(unsafe.Pointer(obj.Ptr))[:obj.len:obj.len]
But this idiom suppresses any possible bounds-checking.
My initial thought was that it might make sense to abuse/extend the [...]foo array syntax for this kind of thing -- have that tell the compiler to use The Actual Available Size, if it can tell, and if it can't, to reject the code as invalid. Or something like that. But that's a mess.
Alternative proposal: unsafe.Slice, which is like unsafe.Pointer, but knows about len and cap.
So, for instance: Given a []byte which holds... something, anyway, possibly data, such as the return value from mmap:
asUint16 := ([]uint16)(unsafe.Slice(data))
This doesn't actually need an explicit setting of len and cap -- it can compute the correct len and cap values from the existing slice. If you do want to slice it, you get bounds checking on the bounds you provide.
This doesn't let you do anything new, exactly, but it does replace one of the more common uses of unsafe.Pointer with a slightly-safer thing, and I think this would functionally replace any real-world usage of SliceHeader I've ever seen with a variant that is guaranteed not to make the stupid mistakes that real programmers do.
This is similar to, but not the same as, 19367. I am thinking of unsafe.Slice as mostly being useful for casting, rather than being a struct that users would interact with as a struct. So you wouldn't access s.Len, you'd call len(s). Or possibly not, because it's meaningless to ask about the length of an abstract slice without a data type in mind.
So, for instance:
uint64s := ([]uint64)(unsafe.Slice(uint16s))```
The `unsafe.Slice` would know that it has a cap of 46 bytes and a length of 34 bytes, so it would produce a []uint64 with len 4 and cap 5.