5 ms·
The Go Blog has an article about slice internals. [0] After reading that, it is a bit easier to see why this happens. Everything is a value in Go, whether it's
by stygiansonic 11y ago
The Go Blog has an article about slice internals. [0]
After reading that, it is a bit easier to see why this happens. Everything is a value in Go, whether it's an int, a struct type or a reference type like a pointer/map/slice. [1]
Since everything is a value, this is not really "aliasing"
// b "aliases" a
b := a
It only appears to be by coincidence: The entire slice data structure gets copied, and one part of that is a pointer to the underlying array, which does not get copied. This is why updating the underlying array (via `a`) reflects in `b`.
Since `append()` either modifies the underlying original array, or allocates a new one if the underlying array is not long enough (classic dynamic array behaviour) [2], the use of this function will only impact `b` if a new array is not allocated.
To be fair, the article mentions all of this, but sort of in passing and at the end. ("They're slices which are a value type that have a pointer within them.") I feel that this is sort of the key point.
0. http://blog.golang.org/go-slices-usage-and-internals http://blog.golang.org/go-slices-usage-and-internals
1. https://blog.golang.org/go-maps-in-action https://blog.golang.org/go-maps-in-action
2. http://golang.org/pkg/builtin/#append http://golang.org/pkg/builtin/#append
- dilap 11y agoYeah exactly, once you take the time to understand how slices work (I definitely recommend those articles to anyone coding much in Go), the behavior is completely unsurprising.
- jules 11y agoThis does not mean it's good behavior. Nothing is surprising after you learn how it works.
- eloff 11y agoIt's not bad behavior. It's expected one you understand what slices are, and it's a better set of tradeoffs than slices in e.g. python which copies the underlying array.
- waps 11y agoThen try this one on for size. Very similar, ... but not quite : package main import "fmt" func main() { // Create a slice of length 1 and capacity 2. Note: try changing this to make([]int, 1, 1) ... a := make([]int, 1, 2) // b "aliases" a b := a fmt.Printf("%v %v\n", a, b) a = append(a, 2) fmt.Printf("%v %v\n", a, b) b = append(b, 5) // Question : what does a contain now ? fmt.Printf("%v %v\n", a, b) } http://play.golang.org/p/Hf2noTyNrp http://play.golang.org/p/Hf2noTyNrp Run the program, change the capacity in the make call to 1, and re-run. If they were values, a and b should simply diverge, so the last line should output [0 2] [0 5]. If they were references, then the last line should output [0 2 5] [0 2 5]. So slices (and maps) are neither values nor references. They're "half-references". Their length is passed as a value, their contents as a reference. Sort of like the linux kernel iovec structures, but completely hidden. I would say this is a bug, as it violates both ways to think about these values. But it can't be changed without changing the complexity of lists in Go ... so this isn't going to change, and therefore will be declared "not a bug".
- echlebek 11y agoI think it's reasonable to ask: "who is this bugging?" When I was learning Go it took me about 5 minutes to understand how slices work, and I've never had a problem with this behaviour in my work (or my coworkers' work). Also, it's very rare to see anyone in the #go-nuts IRC asking for help with slices, which leads me to believe that most people don't really have a problem understanding them. You yourself clearly have a solid understanding of them. The semantics are clearly defined in the spec and the internals are easy to understand if you read the docs.
- SamReidHughes 11y agoThat's not a difficult problem, the behavior is straightforwardly predictable. append says it will use the same slice until it has no more capacity. A slice reference behaves like it's a pointer, size, and capacity. If you're expecting reference b to "know" about what a has done, or vice versa, the problem is not with the language, it's with you.
- Dylan16807 11y ago
- amelius 11y agoCopying a pointer is by definition equal to aliasing.
- barsonme 11y agoA slice is: type sliceStruct struct { array unsafe.Pointer len int cap int } I think you both _mean_ the same thing, just the previous poster is focusing on the entire struct itself, and you're focusing on the 'array' member of the struct. Or I'm just babbling.
- amelius 11y agoMy point was that hiding a pointer in a struct does not magically make the aliasing go away.
- stygiansonic 11y agoThe slice itself is not being aliased, but yes, of course, the underlying array is.