Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slicing a slice pointer passed as argument

Tags:

slice

pointers

go

I have the following code:

func main() {
    var buf []byte{1, 2, 3, 4, 5}
    buf = buf[2:]
    fmt.Println(buf)
    panic(1)
}

However I want to pass a pointer to buf byte slice to another function, and slice it there, so something like:

func main() {
    var buf []byte{1, 2, 3, 4, 5}
    sliceArr(&buf, 2)
    fmt.Println(buf)
    panic(1)
}

func sliceArr(buf *[]byte, i int) {
    *buf = *buf[i:]
}

It gives me an error that I cannot use type []byte as type *[]byte in argument to sliceArr() function, and that I cannot slice type *[]byte. What's wrong? Aren't slices passed by reference by default? I tried to do this without a pointer but it doesn't work - the array is being copied. How can I do this?

like image 285
user99999 Avatar asked Jun 24 '16 12:06

user99999


1 Answers

The error cannot use type []byte as type *[]byte in argument to sliceArr() comes from a typo which you haven't posted (you tried to pass a slice and not a pointer to a slice to sliceArr()).

As to the other error (cannot slice type *[]byte), you just need to use parenthesis to group the pointer dereferencing:

*buf = (*buf)[i:]

And you accidentally left out a = sign from the variable declaration. Other than that, everything works as you wrote it:

func main() {
    var buf = []byte{1, 2, 3, 4, 5}
    sliceArr(&buf, 2)
    fmt.Println(buf)
}

func sliceArr(buf *[]byte, i int) {
    *buf = (*buf)[i:]
}

Output (try it on the Go Playground):

[3 4 5]

Note:

Note that the specification states that if p is a pointer to an array, p[low:high] is shorthand for (*p)[low:high], that is, the pointer is dereferenced automatically for you. This does not happen automatically if p is a pointer to a slice, p[low:high] is invalid as you cannot slice a pointer. So you have to dereference the pointer manually in case of pointers to slices.

Reason for this deviation is that a pointer to a slice is very rare as slices are already "just" descriptors (to a contiguous part of an underlying array), and slices are passed without a pointer most of the time, so if in the rare case (like this one) you do need to pass a pointer, you need to be explicit about handling it. Arrays on the other hand represent all the elements; assigning or passing arrays copies all the values, so it is much more common to use pointers to arrays (than pointers to slices) - hence it is justified to have more language support for working with array pointers.

Also note that your task could be achieved by requiring only a (non pointer) slice as the parameter type and returning the new slice value - which of course must be assigned at the caller (a good example for this is the builtin append() function).

Note #2:

If the parameter type is a slice (and not a pointer to a slice), and you pass a slice, the underlying array is not copied, only the slice header. But in this case if you slice the argument (which is a copy of the slice header), and you assign back the new slice (slice header) to the argument, you are just changing the value of the parameter (a local variable) and not the original value (variable) that was passed, this is why it won't work.

Read the following blog posts for more details on slices:

Go Slices: usage and internals

Arrays, slices (and strings): The mechanics of 'append'

like image 172
icza Avatar answered Sep 19 '22 17:09

icza