go icon indicating copy to clipboard operation
go copied to clipboard

proposal: reflect: add UnsafeSlice function

Open ianlancetaylor opened this issue 2 years ago • 14 comments

In Go 1.21 we are introducing some new functions in the unsafe package: unsafe.Slice, unsafe.SliceData, unsafe.String, unsafe.StringData. It should be possible to access this functionality through the reflect package as well.

The value returned by unsafe.SliceData is already available by calling the reflect.Value.Pointer method.

I propose that we extend reflect.Value.Pointer so that if it is called on a value of kind String, it returns the equivalent of unsafe.StringData.

The equivalent to unsafe.String is simply reflect.ValueOf(unsafe.String((*byte)(vp.UnsafePointer()), n)), so I don't think we need to add anything for that.

I propose one new function:

package reflect

// UnsafeSlice returns a Value representing a slice whose underlying data starts at vp,
// with length and capacity equal to n. vp.Kind() must be Pointer.
// The returned slice will have element type vp.Type().Elem().
// This is like unsafe.Slice.
func UnsafeSlice(vp Value, n int) Value

(edited to change function name from Slice to UnsafeSlice`)

ianlancetaylor avatar Jul 11 '23 22:07 ianlancetaylor

It seems to me that the new functionality is to MakeSlice as NewAt is to New. So I suggest:

func SliceAt(typ Type, p unsafe.Pointer, len int) Value

That has the additional advantage of passing through the type unsafe.Pointer, which clarifies that it is in fact unsafe.

bcmills avatar Jul 12 '23 02:07 bcmills

Why does the proposed reflect.Slice (or reflect.SliceAt) require the length and capacity to be the same instead of having two separate parameters like reflect.MakeSlice?

rittneje avatar Jul 12 '23 02:07 rittneje

Those functions were introduced starting in 1.17 and then more in 1.20.

The equivalent to unsafe.String is simply reflect.ValueOf(unsafe.String(vp.Pointer(), n)), so I don't think we need to add anything for that.

That won't work. unsafe.String() requires a *byte, not a uintptr. The conversion requires first converting through an unsafe.Pointer, so I think having an equivalent top-level function makes sense for that one, too. It's not really any different from a slice otherwise except for the type always being the same:

reflect.ValueOf(unsafe.String((*byte)(unsafe.Pointer(v.Pointer())), v.Len()))
reflect.ValueOf(unsafe.Slice((*T)(unsafe.Pointer(v.Pointer())), v.Len()))

DeedleFake avatar Jul 12 '23 03:07 DeedleFake

Why does the proposed reflect.Slice (or reflect.SliceAt) require the length and capacity to be the same … ?

Because unsafe.Slice does too. Most of the time you do want them to be the same, and if you want to reduce the capacity that's easy enough to do as a separate operation (via SetCap).

bcmills avatar Jul 12 '23 04:07 bcmills

For the string case,

reflect.ValueOf(unsafe.String(v.Interface().(*byte), v.Len()))

or

reflect.ValueOf(unsafe.String((*byte)(v.UnsafePointer()), v.Len()))

will often suffice.

bcmills avatar Jul 12 '23 04:07 bcmills

I think the function name should be UnsafeSlice instead of Slice.

Slice is already taken as a Kind.

cuonglm avatar Aug 08 '23 05:08 cuonglm

Change https://go.dev/cl/516597 mentions this issue: reflect: add UnsafeSlice

gopherbot avatar Aug 08 '23 06:08 gopherbot

Change https://go.dev/cl/516596 mentions this issue: reflect: handle String kind in Value.Pointer

gopherbot avatar Aug 08 '23 06:08 gopherbot

I think the function name should be UnsafeSlice instead of Slice. Slice is already taken as a Kind.

Another reason is that these are in fact unsafe, so the name should begin with Unsafe to distinguish it from the normal, safe reflect API.

rsc avatar Feb 07 '24 18:02 rsc

It seems like we should also update UnsafePointer to handle String (not just update Pointer).

rsc avatar Feb 07 '24 18:02 rsc

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings. — rsc for the proposal review group

rsc avatar Feb 09 '24 00:02 rsc

Have all remaining concerns about this proposal been addressed?

Two parts to the proposal.

First, add func UnsafeSlice, mimicking unsafe.Slice.

Second, update Value.Pointer and Value.UnsafePointer to handle String.

rsc avatar Feb 14 '24 23:02 rsc

I don't think the signature concern in https://github.com/golang/go/issues/61308#issuecomment-1631737298 has been addressed yet.

The other functions and methods in the reflect package that use the word Unsafe still explicitly require the use of the unsafe package, but

func Slice(vp Value, n int) Value

(as described in https://github.com/golang/go/issues/61308#issue-1799861635) does not have that property.

bcmills avatar Feb 15 '24 16:02 bcmills

I'm not sure that's quite true of the existing reflect.Value.UnsafePointer method, which does return a value of type unsafe.Pointer but can be used without importing the unsafe package.

Note that the proposed function is now named UnsafeSlice rather than Slice.

ianlancetaylor avatar Feb 16 '24 01:02 ianlancetaylor

I also prefer @bcmills ' alternate signature. It has the clear advantage of using unsafe.Pointer, and I suspect in practice it will be more common that callers will have an unsafe.Pointer already, rather than a Value, so it will save the conversion (though they can convert either way).

aclements avatar Feb 28 '24 18:02 aclements

Have all remaining concerns about this proposal been addressed?

Two parts to the proposal.

First, add func SliceAt, analogous to NewAt (which probably should have been called ValueAt) but for slices:

func SliceAt(typ Type, p unsafe.Pointer, len int) Value

Second, update Value.Pointer and Value.UnsafePointer to handle String.

rsc avatar Mar 08 '24 04:03 rsc

Based on the discussion above, this proposal seems like a likely accept. — rsc for the proposal review group

Two parts to the proposal.

First, add func SliceAt, analogous to NewAt (which probably should have been called ValueAt) but for slices:

func SliceAt(typ Type, p unsafe.Pointer, len int) Value

Second, update Value.Pointer and Value.UnsafePointer to handle String.

rsc avatar Mar 15 '24 01:03 rsc

Change https://go.dev/cl/572117 mentions this issue: reflect: Value.Pointer and Value.UnsafePointer handle String

gopherbot avatar Mar 16 '24 13:03 gopherbot

Change https://go.dev/cl/572118 mentions this issue: reflect: add SliceAt

gopherbot avatar Mar 16 '24 14:03 gopherbot

Does reflect.SliceAt do all the runtime validations that unsafe.Slice do? For example, should this code panic?

_ = SliceAt(TypeOf(0), unsafe.Pointer((*int)(nil)), 1)

cuonglm avatar Mar 20 '24 16:03 cuonglm

Yes, reflect.SliceAt should be no more unsafe than unsafe.Slice. So it should do the same validations.

rsc avatar Mar 27 '24 17:03 rsc

No change in consensus, so accepted. 🎉 This issue now tracks the work of implementing the proposal. — rsc for the proposal review group

Two parts to the proposal.

First, add func SliceAt, analogous to NewAt (which probably should have been called ValueAt) but for slices:

func SliceAt(typ Type, p unsafe.Pointer, len int) Value

Second, update Value.Pointer and Value.UnsafePointer to handle String.

rsc avatar Mar 27 '24 18:03 rsc

Change https://go.dev/cl/575956 mentions this issue: reflect: add missing String case in Value.UnsafePointer doc

gopherbot avatar Apr 03 '24 04:04 gopherbot

Change https://go.dev/cl/592197 mentions this issue: doc/next: improve description of proposal 61308

gopherbot avatar Jun 12 '24 17:06 gopherbot