Skip to content

unsafe docs and SliceHeader: I am not sure what is defined here #33794

@seebs

Description

@seebs

What version of Go are you using (go version)?

1.12, but N/A

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

N/A

What did you do?

Read the documentation for unsafe.

What did you expect to see?

Something that did not confuse me.

What did you see instead?

Something that confused me.

In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types.

This has an accompanying example:

// INVALID: a directly-declared header will not hold Data as a reference.
var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost

What's not obvious to me: Is the only reason this is "INVALID" that p might already be lost, such that a runtime.KeepAlive(p) after the last line would have saved it? Or is this also intended to cover the possibility that some future version of Go may have additional unspecified magic in a slice object which is not in the SliceHeader parts, such that simply creating a SliceHeader (or a StringHeader) doesn't actually make a thing which could be a valid object?

I've been writing code which has a pointer (which is persistent, and in a data structure which survives past the current scope, thus at no risk of being garbage collected), and which does something to the effect of:

asUint16 := *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(obj.ptr)), Len: int(obj.len), Cap: int(obj.len)}))

In fact, obj.ptr isn't going away, so I'm not worried about garbage collection. But if an actual Slice were not exactly the same size as a SliceHeader, but rather, the SliceHeader were just a prefix of the actual internal slice representation... This code would in fact be just plain wrong, because other code might try to access the "rest" of the slice object, and not find it.

Instead, I'd need to do something like:

var u []uint16
h := (*reflect.SliceHeader)&u
h.Data, h.Len, h.Cap = uintptr(obj.ptr), obj.len, obj.len

Which is, I suppose, certainly no harder to read, but I'm not sure whether it offers any semantic difference, apart from u holding the reference to the pointer, in a way that a temporary slice header object wouldn't.

(Related-ish: #20171, #19367.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions