结构切片的属性作为函数参数而不分配切片

I have a slice of structs that looks like:

type KeyValue struct {
  Key   uint32
  Value uint32
}

var mySlice []Keyvalue

This slice mySlice can have a variable length.

I would like to pass all elements of this slice into a function with this signature:

takeKeyValues(keyValue []uint32)

Now I could easily allocate a slice of []uint32, populate it with all the keys/values of the elements in mySlice and then pass that slice as an argument into takeKeyValues()...

But I'm trying to find a way of doing that without any heap allocations, by directly passing all the keys/values on the stack. Is there some syntax trick which allows me to do that?

There is no safe way to arbitrarily reinterpret the memory layout of the your data. It's up to you whether the performance gain is worth the use of an unsafe type conversion.

Since the fields of KeyValue are of equal size and the struct has no padding, you can convert the underlying array of KeyValue elements to an array of uint32.

takeKeyValues((*[1 << 30]uint32)(unsafe.Pointer(&s[0]))[:len(s)*2])

https://play.golang.org/p/Jjkv9pdFITu

I a bit puzzled by what you want. If the slice is dynamic, you have to grow it dynamically as you add elements to it (at the call site).

Go does not have alloca(3), but I may propose another approach:

  1. Come up with an upper limit of the elements you'd need.
  2. Declare variable of the array type of that size (not a slice).
  3. "Patch" as many elements you want (starting at index 0) in that array.
  4. "Slice" the array and pass the slice to the callee.

Slicing an array does not copy memory — it just takes the address of the array's first element.

Something like this:

func caller(actualSize int) {
  const MaxLen = 42

  if actualSize > MaxLen {
    panic("actualSize > MaxLen")
  }

  var kv [MaxLen]KeyValue

  for i := 0; i < actualSize; i++ {
    kv[i].Key, kv[i].Value = 100, 200
  }

  callee(kv[:actualSize])
}

func callee(kv []KeyValue) {
  // ...
}

But note that when the Go compiler sees var kv [MaxLen]KeyValue, it does not have to allocate it on the stack. Basically, I beleive the language spec does not tell anything about the stack vs heap.

As an alternative to JimB's answer, you can also use the reflect (reflect.SliceHeader) and unsafe (unsafe.Pointer) packages to achieve this.

https://play.golang.org/p/RLrMgoWgI7t

s := []KeyValue{{0, 100}, {1, 101}, {2, 102}, {3, 103}}
var data []uint32
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
sh.Data = uintptr(unsafe.Pointer(&s[0]))
sh.Len = len(s) * 2
sh.Cap = sh.Len
fmt.Println(data)