Live data from Hacker News

a[low:high:max] in Golang – A Rare Slice Trick

build-your-own.org

1–10 of 131 posts

Re: a[low:high:max] in Golang – A Rare Slice Trick

#2
This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest.

> This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

Re: a[low:high:max] in Golang – A Rare Slice Trick

#3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

Re: a[low:high:max] in Golang – A Rare Slice Trick

#4
post #3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

I know even less go but it sounds like it prevents accidentally writing past the end of the array by making a copy and appending to that.

Re: a[low:high:max] in Golang – A Rare Slice Trick

#5
post #3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

There are no immutable arrays. If you have a slice of capacity ten with four elements, you can write a fifth element to it. If you reduce the capacity to four, then a new slice must be allocated to store five elements.

The backing store for a slice is just a pointer. The rule is you don't give people pointers that you don't want them to write to.

Re: a[low:high:max] in Golang – A Rare Slice Trick

#6
post #3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

Append mutates or copies-and-appends based on the capacity of the slice it's given. And since slicing an array or a slice gives you just a view, not a copy, it can cause "spooky action at a distance" and mutate things you didn't expect, especially if it was a slice with extra capacity of data used somewhere else. Which is what this article is describing.

Plus the knowledge of the change in length (to see the appended data) is only visible with the returned slice (a new view with the larger length), but the underlying data is changed either way.

It's not often a source of errors, but when it is it can be extremely hard to diagnose.

Re: a[low:high:max] in Golang – A Rare Slice Trick

#7
post #3

Earlier quoted context omitted.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

There are no immutable arrays. If you have a slice of capacity ten with four elements, you can write a fifth element to it. If you reduce the capacity to four, then a new slice must be allocated to store five elements. The backing store for a slice is just a pointer. The rule is you don't give people pointers that you don't want them to write to.

That's just not true. There are immutable arrays in length. [4]int cannot be appended to. The backing of all slices are array types and array doubling is used for appends that go beyond the capacity.

https://go.dev/ref/spec#Array_types

Re: a[low:high:max] in Golang – A Rare Slice Trick

#8
post #3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

Go has no way to ensure "const correctness". If you pass around a slice (which you'll be doing a lot, since it's essentially Go's equivalent of a vector), everyone can modify the slice however they please. It's just a fat pointer.

So yes, something which should be treated as immutable.

The way Go works here easily leads to bugs, especially when concurrency is involved and slices get captured or used by goroutines.

If you send a slice to a function, they essentially receive a pointer pointing at the same block of memory as in your calling function. They can modify it. If they append to the slice, however, and surpass the capacity of the slice they received, then their slice gets reallocated and doesn't point at the same block of memory anymore.

In other words, if you receive a slice, append to it, and then change the first value of the slice, then this might modify a slice (or array) somewhere completely different, depending on whether your append surpasses the allocated capacity of the slice or not.

Welcome to Golang.

Re: a[low:high:max] in Golang – A Rare Slice Trick

#9
post #3

Earlier quoted context omitted.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

Go has no way to ensure "const correctness". If you pass around a slice (which you'll be doing a lot, since it's essentially Go's equivalent of a vector), everyone can modify the slice however they please. It's just a fat pointer. So yes, something which should be treated as immutable. The way Go works here easily leads to bugs, especially when concurrency is involved and slices get captured or used by goroutines. If…

But also strings are actually immutable sometimes (at the very least when hardcoded), and they can be converted to a `[]byte` slice that looks mutable, but panics when modified. AFAIK no other slice-like data in Go behaves like this.

Fun!

Re: a[low:high:max] in Golang – A Rare Slice Trick

#10
post #3

This paragraph is scary. It looks like a good idea for the Golang version of the Underhanded C Contest. > This trick is useful for returning a slice from an immutable array; if you accidentally append to the supposedly immutable slice, a copy is forced and no data is overwritten because there is no more capacity left.

I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?

Here's a succinct demonstration: https://go.dev/play/p/Li6_Rpe2R5L

Basically: a slice is a triple of {ptr to allocation, number of elements used, size of allocation}

the slices will share the same underlying allocation. By specifying the third parameter in the slice function, you set the capacity equal to the number of elements in your slice. This forces the next append to reallocate and copy the contents into a new region.

Without bounding the capacity when creating a new slice, the append operation could possibly continue using the same allocation, shared with the original slice. It could possibly resize and copy as well. It depends on how full the slice is, and is an implementation detail subject to change between versions.

Post reply on HN