2

Given a slice (not a pointer to a slice!) is there any way to truncate it in place?

The naive implementation doesn't work, because of scope:

package main

import (
    "fmt"
)

func truncate(s []int, to int) []int{
    s = s[:to] # <- has no effect outside this function
    return s
}

func main() {
    s := []int{0, 1, 2, 3, 4}
    s1 := truncate(s, 3)
    fmt.Printf("%v\n", s1)
    fmt.Printf("%v\n", s)
}

prints

[0 1 2]
[0 1 2 3 4] # <- can we get [0 1 2] here?

Is there any way to modify the length or capacity of an existing slice, or are they immutable?


ETA: I thought this was obvious enough, but apparently not: when I ask whether it's possible to do this in place, I mean without reassigning s.

David Moles
  • 48,006
  • 27
  • 136
  • 235
  • 2
    No. The slice length and capacity are not accessible parameters. The slice operations (e.g. `s[:to]`) return a new slice of the same underlying array. – Adrian Jun 06 '19 at 19:46
  • @Adrian If you can link to an authoritative reference and make that an answer, I'll mark it accepted. – David Moles Jun 06 '19 at 19:50
  • 6
    _"Given a slice"_ – given how? You receive it as a function parameter? That value is a copy of the original slice, and it is a different value with no "connection" to the original. So you're already at a lost cause. See [Remove from slice inplace in Golang](https://stackoverflow.com/questions/56394632/remove-from-slice-inplace-in-golang/56394697#56394697); and [Are golang slices pass by value?](https://stackoverflow.com/a/39993797/1705598); and also [slice vs map to be used in parameter](https://stackoverflow.com/a/47590531/1705598). – icza Jun 06 '19 at 20:25
  • 2
    See the spec: https://golang.org/ref/spec#Slice_expressions – Peter Jun 06 '19 at 20:26
  • 2
    An authoritative reference saying you *can't* do that? I don't think there is one; the spec doesn't enumerate the things you can't do, only the things you can. – Adrian Jun 06 '19 at 21:05
  • 4
    Response to ETA: The only way to change a value in Go is to assign to the value. Function `truncate` cannot change variable `s` in `main` because `truncate` does not have access to that variable. – Charlie Tumahai Jun 06 '19 at 21:16

2 Answers2

3

This is the way to go:

package main

import "fmt"

func main() {
    s := []int{0, 1, 2, 3, 4}
    s = truncate(s, 3)
    fmt.Println(s) // [0 1 2]
}
func truncate(s []int, to int) []int {
    return s[:to]
}

Slice is like a window to an underlying array.


The other way using pointer to the slice:

package main

import "fmt"

func main() {
    s := []int{0, 1, 2, 3, 4}
    truncate(&s, 3)
    fmt.Println(s) // [0 1 2]
}

func truncate(s *[]int, to int) {
    *s = (*s)[:to]
}
wasmup
  • 14,541
  • 6
  • 42
  • 58
  • If you read the question more carefully, this is exactly what I'm not trying to do. There's a reason why the example has the actual reslice inside the `truncate()` function. – David Moles Jun 06 '19 at 20:15
  • 2
    There is no other way. slice is just a struct containing `len`, `cap`, and a pointer to the underlying array. you may use a pointer to the slice and re-slice it inside the function or this is the only way to go. – wasmup Jun 06 '19 at 20:42
0

This can't be done, because slices are passed by value.

Note that as of April 2013, the Go language specification no longer refers to slices, maps, and channels as "reference types". (The behavior didn't change, just the language used to describe it.)

It can't be done with arrays either, because an array's length is part of its type.

David Moles
  • 48,006
  • 27
  • 136
  • 235