Please answer these questions before submitting your issue. Thanks!
go version)?1.6.3 and 1.8.1 Playground
go env)?OSX
I tried to generate a new slice from an existing slice minus 1 item while preserving order using the most intuitive code from your wiki:
https://github.com/golang/go/wiki/SliceTricks#delete
The code causes unintended side effects in the original slice. I did some other tests and these side effects are only present with structs.
Here is an example:
https://play.golang.org/p/f4TxGPsMMY
Code:
package main
import (
"fmt"
)
type Example struct {
ID int
}
func main() {
ns := []Example{Example{1},Example{2},Example{3}}
for j, n := range ns {
if n.ID == 2 {
ns2 := append(ns[:j], ns[j+1:]...)
fmt.Printf("Test? %v ?= %v\n", ns, ns2)
break
}
}
is := []int{1,2,3}
for j, _ := range is {
if j == 2 {
is2 := append(is[:j], is[j+1:]...)
fmt.Printf("Test? %v ?= %v\n", is, is2)
break
}
}
}
Outputs:
Test? [{1} {3} {3}] ?= [{1} {3}]
Test? [1 2 3] ?= [1 2]
Please make a note in the wiki if this is intended or fix please.
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
Test? [{1} {2} {3}] ?= [{1} {3}]
Test? [{1} {3} {3}] ?= [{1} {3}]
Note that you slice on j=1 in the struct slice and you slice on j=2 in the int slice so the two examples are not equivalent. In fact if you slice on 1 on the int slice you'll get the same thing:
Test? [{1} {3} {3}] ?= [{1} {3}]
Test? [1 3 3] ?= [1 3]
Also the trick in the wiki shows that you have to overwrite the original slice:
a = append(a[:i], a[i+1:]...)
while you are assigning to a new variable. We could stress that you need to assign back to a but it's already pretty clear from the example IMHO.
@ALTree You are right, a more equivalent example shows it's broken regardless of type of slice.
I would argue there is not enough context around the decision to re-assign. I'm still not clear why this is necessary.
It's non-intuitive for slice expressions to mutate the original slice. If it's not a bug, but intended behavior, it's not documented anywhere I can tell, neither in the spec nor that particular wiki page.
See https://blog.golang.org/slices .
This is not a bug, so closing.
@ianlancetaylor Could you point specifically to the point in that blog post this semantic is substantiated? I've read it multiple times while learning the language and still took me completely off guard and feels very much like a bug.
For questions about Go, see https://golang.org/wiki/Questions.
@ianlancetaylor @bradfitz I can appreciate it's not a bug, but could you point out the section that's relevant in that post or wiki? I'm familiar with these materials (having read them years ago and returned many times) and I apologize for being dense but I still fail to grok why using a slice expression would mutate the slice the expression acts on. As far as I can see, nothing is ever mentioned about mutation of the original slice value.
I'm not a novice -- I've used Go for several years professionally and this was definitely a head-scratcher for me and my team.
@bradfitz I too would appreciate a pointer to the documentation on this one; it's pretty rare not just in go but in programming in general for set functions to modify the original set. Typically a new set is returned instead. If this is a known semantic in Go, I'd really like to understand how to tell what's going to happen while I'm coding it.
The slice expression does not mutate the underlying array. It's the call to append that mutates it.
(And if you're reading the original example, make sure you read the first comment to understand how the two loops look similar at first glance but are actually different.)
In other words, this is because append will re-use the underlying array (since result will fit into it) for the new slice returned. Thanks for the clarification!
@bradfitz Language like
If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated.
...is what has mislead us to believe append acted in a more functional manner, returning a new slice. Sorry for the trouble!
Most helpful comment
@ALTree You are right, a more equivalent example shows it's broken regardless of type of slice.
I would argue there is not enough context around the decision to re-assign. I'm still not clear why this is necessary.
It's non-intuitive for slice expressions to mutate the original slice. If it's not a bug, but intended behavior, it's not documented anywhere I can tell, neither in the spec nor that particular wiki page.