Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Go's slice behavior reveals a concerning trade-off in language design where performance was prioritized over predictability. Consider this trap:

    a := make([]int, 1, 10)
    a[0] = 42
    b := append(a, 17)
    c := append(a, 37)
    // Surprise! b and c both equal [42, 37]
The fact that append()'s behavior changes based on a hidden property (capacity) violates what I consider a core principle of language design: fundamental operations should be predictable and easy to reason about without having to think about implementation details.

While I understand the performance benefits of reusing memory, language fundamentals shouldn't require developers to constantly think about backing array capacity to avoid bugs. This is especially problematic because it "usually works" until it doesn't - the worst kind of bug.

Modern languages have shown us better ways. Rust makes ownership explicit. Immutable-by-default languages prevent such surprises entirely. Even if Go wanted to maintain performance, they could have made this behavior more explicit - perhaps with separate "append_safe" and "append_reuse" functions.

Programming language fundamentals should be like good UI design - they should do what you expect them to do. When core operations have surprising edge cases, it creates cognitive overhead that ripples through all code written in that language.



Usually the idiom is using the same variable on LHS.

``` a = append(a, elem) ```

If you assign it to anything else, `a` might still remain with the older slice memory which will cause worse problems than equality comparison.

So the kind of code you wrote is hardly ever written in production.

The advantage of having this is avoiding one more heap allocation for slice, which is a good tradeoff if you ask me.


In production almost every

`foo, err := callSomeFn()`

is followed by

``` if err != nil { return err } ```

too; it’s the ones that get missed that are the problem because convention will do nothing to prevent silly mistakes.

Like many things in Go, there are steps the language could take but chooses not to. For example, Swift implements copy-on-write for variable-length arrays. I’m not qualified to comment on whether the Go team is right or wrong in their decisions, but these aren’t unsolved problems.


Honestly, I couldn't believe that this code works as you stated it does. But I ran it in the Playground[17] and sure enough! That is exactly what it produces. Ugh!

[37] https://go.dev/play/p/rO7rlvK7Dna


oh i never thought about y = append(x, ...) in which scenario would make sense to have different x and y?


wow.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: