Live data from Hacker News

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

build-your-own.org

31–40 of 131 posts

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

#31
post #24

Earlier quoted context omitted.

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…

On the bright side, if one tests with race detection enabled these problems are usually made apparent.

This issue can trivially occur in sequential code.

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

#32
post #9

Earlier quoted context omitted.

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!

This is misleading enough it's a lie; to do this you need to use a function named `unsafe.Pointer` with a type named `reflect.StringHeader`, not just `[]byte` and `string`.

It means there is special protection for string-data that is not available to anything else, and that library-authors get panic reports about immutable byte slices. To that degree, it's something that the specialized type exposes you to but the type system does not adequately protect or signal.

But yes, AFAIK this requires use of `unsafe`. Which does change things, and puts the blame for misuse squarely on the immutable-byte-slice creators.

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

#33
post #21
post #20

Earlier quoted context omitted.

So in other words, appending to a slice may overwrite data in the middle of a different slice?

Yep. Depending on how the slice was constructed , not how it is used. Slices (largely) behave like this in most languages, Go's contribution is mostly that slices and append are ubiquitous, so pretty much every Go coder is exposed to it. Prior to generics, literally any alternative was so much more work they essentially haven't been used. That may change in the future now that we do have (very simplistic) generics, b…

> Slices (largely) behave like this in most languages

Only on assignment, not in appending.

> Go's contribution is mostly that slices and append are ubiquitous

Go's contribution is the conflation of slices and vectors, which in most languages are separate (or really most languages only have the latter and don't provide access to backing arrays, thus precluding this specific confusion).

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

#34
post #26

My fave golang slice trick is the len of an empty slice is 0, but the slice itself is == to nil, but the len of nil won't compile. Can't understand that one. https://go.dev/play/p/MslCkBphl7q?v=gotip

Because nil is a special thing in Go.

nil is not a value, it's a predeclared identifier.

it represents zero value for pointers, interfaces, maps, slices, channels and function types, representing an uninitialized value

len(nil) feels like it should work if you think of nil as the same as "value of empty array"

but what should be:

  var p *Struct
  len(p) 
  ???
There's no "length of zero-valued pointer".

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

#35

0. Only 2 of 3 values are necessary. 1. Can 1 value be unspecified? 2. Can 2 values be unspecified? 3. Can 3 values be unspecified? 4. Are conflicting values interpreted as intersection of ranges rather than union? Note 1-3. With 2 values, I believe x[:] is how to lift a sized array into a more generic slice type.

Your questions are really unclear.

In the 2-parameters form `a[low:high]`, both values can be left out, defaulting to respectively 0 and len(a). In the 3-parameter form, only the leading value can be left out, defaulting to 0.

I've no idea what (4) is asking about.

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

#37
post #21

Earlier quoted context omitted.

Yep. Depending on how the slice was constructed , not how it is used. Slices (largely) behave like this in most languages, Go's contribution is mostly that slices and append are ubiquitous, so pretty much every Go coder is exposed to it. Prior to generics, literally any alternative was so much more work they essentially haven't been used. That may change in the future now that we do have (very simplistic) generics, b…

Slices (largely) behave like this in most languages Is that really the case? I can’t think of many languages that let you construct a view into the middle of array, pass that view as an argument to a function call, and allow that function to add new values into your array via the view. In Python or JS, for example, the “slice” would just be a brand new array, right?

If the slice syntax creates a copy, I would argue that it's simply syntactic sugar for array copying, not actually producing a slice (i.e. there is no slice type). But yes, `somefunc(ary[1:])` in Python produces a copy, not a reference to the underlying value. You could build a more "true" slice class, but the builtin stuff doesn't do that. JavaScript is similar.

Java however has `List.sublist` which largely behaves like Go: https://docs.oracle.com/javase/8/docs/api/java/util/List.htm... . Sometimes these kinds of things are also referred to as "views". Go calls them slices though, and this is a Go article.

edit: C# apparently has "spans": https://learn.microsoft.com/en-us/archive/msdn-magazine/2018...

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

#38
post #11
post #9

Earlier quoted context omitted.

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!

This is a pretty good vignette of how Go is designed. "Users don't need this feature (immutability, polymorphism, etc.), let's not include it. Ah crap, turns out we actually need it for a core language feature. Is there a lesson we can take away here? Nah, just make an ad-hoc implementation of the functionality in this one place."

For what it’s worth it sounds no worse than Java: you have immutable strings but you have no way to enforce that you can’t modify something that you have recieved, which is why the underlying char array for a string has to be defensively copied if the user asks for `asArray()` or whatever it is.

Strings being immutable doesn’t seem like a special case, though. The underlying mutable array is just encapsulated, which is something that you can implement yourself. (But reflection… maybe you can break the rules with reflection.)

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

#39
post #11
post #9

Earlier quoted context omitted.

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!

This is a pretty good vignette of how Go is designed. "Users don't need this feature (immutability, polymorphism, etc.), let's not include it. Ah crap, turns out we actually need it for a core language feature. Is there a lesson we can take away here? Nah, just make an ad-hoc implementation of the functionality in this one place."

Const is a hard language feature to design well. There are a lot of pitfalls in the way const is used in C++ and TypeScript (I’m including “readonly” in the discussion).

Rust gets it right, but Rust is relatively complicated.

The languages which are most similar to Go are Java and C#, both of which also lack const types, or have a very limited version of constness.

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

#40
post #32

Earlier quoted context omitted.

This is misleading enough it's a lie; to do this you need to use a function named `unsafe.Pointer` with a type named `reflect.StringHeader`, not just `[]byte` and `string`.

It means there is special protection for string-data that is not available to anything else, and that library-authors get panic reports about immutable byte slices. To that degree, it's something that the specialized type exposes you to but the type system does not adequately protect or signal. But yes, AFAIK this requires use of `unsafe`. Which does change things, and puts the blame for misuse squarely on the immuta…

> It means there is special protection for strings that is not available to anything else

Lots of languages have no C/C++-style const objects yet immutable strings. It's weird to pick on Go specifically for this. (Since it's the JVM model, at this point it may even be a majority of mainstream languages.)

If you mean specifically the panic when modifying a static string, is this not just the default `mprotect` on rodata? You can do that to any memory page you want. It's a feature of the kernel's memory management, not the type system or even the language runtime.

> library-authors get panic reports about immutable byte slices.

I'm really skeptical this happens in any meaningful amount. Go offers the feature to transform a byte slice you know you won't want anymore into a string without a memory allocation; or to pass a string to a function which wants a []byte and will not mutate it, usually to wrap an optimized implementation working on both strings and []byte. Modifying a string's contents is always undefined behavior, even if it doesn't panic immediately - the compiler will assume strings are immutable and make "as if" judgements accordingly.

Post reply on HN