top | item 35215217

(no title)

H4ZB7 | 3 years ago

why do people write articles about go features? when PHP was in its prime, almost nobody wrote blogs explaining how they found some philosophy in PHP. there's a reason for that.

when you see someone open their article explaining a language feature by talking of the implementation details or specific use cases, that's a language smell (of course all industrial PLs stink).

ironically go is the only post 80s language that uses "memory safe" as a marketing point (even though they all are), yet go has the most memory unsafety of post 80s industrial languages. you can parse something and pass on a slice somewhere. if you mistakenly slice that slice with a bigger size - this incorrect size being the programmer bounds check error - you restore some of the original array that was supposed to be cut off and teh next operation working on that slice will thus modify or leak data:

    package main
    import "fmt"
    func main(){
            a := [3]int{1,2,3}
            b := a[0:2]
            fmt.Println(b[1])
            c := b[0:3]
            fmt.Println(c[2])
    }

    $ go run a.go
    2
    3
the other example of memory unsafety in go being that modifying slices between threads can lead to actual memory corruption, not just simulated memory corruption as above

the point here is that this footgun doesnt even have a real point outside of some insane performance argument. nobody would ever design something like this without massive cognitive dissonance (aside from industrial PLs, which just copy and modify the previous industrial PL, C in this case). all go's primitives are rigged like this with unintuitive behaviors. its amazing how much such a simple language with small scope can get wrong. and i expect nothing less from people who go around saying "zeroeth". DAY OF THE BOBCAT SOON

discuss

order

beltsazar|3 years ago

Yeah, the most surprising thing about Go's slice expressions is that you can reslice a slice beyond its length, as long as it's still within its capacity.

I wonder how many off-by-one bugs have happened undetected because a slice is unintentionally resliced beyond its length. Instead of crashing, so that the issue is known early, the program will still run with inconsistent data.

preseinger|3 years ago

calling articles about language features and/or their implementations a "smell" is some pretty insane stuff

the slice behavior you demonstrate there is well-defined by the language spec, it's totally memory safe, it doesn't demonstrate memory corruption or anything like that

go is probably the most successful new language since java, if you don't like it that's fine, but it's nonsensical to call its design decisions "wrong"

iainmerrick|3 years ago

I’m pretty sure the most successful language since Java is JavaScript.

Of course it has even more bad design decisions than Go, but people don’t leap out to defend them quite so much.

silisili|3 years ago

I'm not sure how your snippet above exemplifies memory unsafety.

Concurrent access does let you hit some 'fun' behavior, but you have to be doing pretty dumb things to hit them. And while the implementation may be able to save you from something like that, such things would likely bubble up elsewhere(disk i/o, network i/o, etc) if doing that kind of thing.

DangitBobby|2 years ago

I would also consider this to be memory unsafe. If you have an "array", you should not be able to index (or slice) beyond its bounds. If you are allowed to do so, you may have unpredictable junk in your array.

kosherhurricane|3 years ago

TLDR:

The best part of Go is that there is very little magic in Go. If you understand that slices are just fat pointers implemented as a built-in, there is nothing confusing about them. I can understand every part of a Go program, all the way down to the language syntax that generate assembly. I don't have to be afraid of or be mystified by any language feature, because 1) there are few, 2) they are just programs implementable in Go. This does not happen with many languages.

Longer version:

Go didn't need to add slices as a language feature (it could have been a library function of containers, as fat pointers are not a new thing), but having it in the language makes using them easy. And not having generics at the start sort of forced their hand.

And as slices are just fat pointers to an underlying array, obviously it's not multi-thread safe.

So if you understand that slices are just C-style structs with pointer to data, a length counter and a capacity counter, then nothing in your example code is surprising. There is no hidden memory copy, no hidden synchronization lock to make it thread safe. And Go's a = append(a, item) now makes sense, because if 'a' grew in size, append would have to create a new underlying array, and a new slice struct with a pointer to new data. To me, it's much easier to reason about what the code is doing than other languages with Array types.

> nobody would ever design something like this without massive cognitive dissonance

Somebody did, without any cognitive dissonance. And I like it :)

> just copy and modify the previous industrial PL, C in this case

Go really wanted to be "A Better C". The language is not much larger than C, removed a bunch of C foot-guns, and it's as capable as Java, if not a bit more. I think the compromises Go made were well considered compared to other C family of languages.

beltsazar|3 years ago

> The best part of Go is that there is very little magic in Go. If you understand that slices are just fat pointers implemented as a built-in, there is nothing confusing about them.

It's just a tautology. If you understand something, of course by definition you aren't confuse about them. By using the same logic, all languages have "very little magic."

the_gipsy|2 years ago

How can you call slices "little magic" when append may or may not modify the original slice?

So you pass something as a value, and something else as a pointer. But slices are passed as values but sometimes act like pointers.

andromeduck|2 years ago

Why not just use c at that point?

hamdouni|2 years ago

Oh, I rather use this version :

  package main

  func main() {
    a := [2]int{1, 2}
    b := a[0:1:1]
    c := b[0:2]
    println(b[0], c[1])
  }