Comment by culebron21

16 hours ago

> It has proper enums.

Well, then they look awkward and have give a feel like it's a syntax abuse.

> Its arrays and slices are exactly the same as how you would do it in C. So while it is true that trips up many coming from languages that wrap them in incredible amounts of magic, but the issue you point to here is actually a lack of magic.

In Rust, I see exactly what I work with -- a proper vector, material thing, or a slice, which is a view into a vector. Also, a slice in Rust is always contiguous, it starts from element a and finishes at element b. I can remove an arbitrary element from a middle of a vector, but slice is read-only, and I simply can't. I can push (append) only to a vector. I can insert in the middle of a vector -- and the doc warns me that it'll need to shift every element after it forward. There's just zero magic.

In Go instead, how do I insert an element in the middle of an array? I see suggestions like `myarray[:123] + []MyType{my_element} + myarray[123:]`. (Removing is like myarray[:123] + myarray[124:]`.)

What do I deal in this code with, and what do I get afterwards? Is this a sophisticated slice that keeps 3 views, 2 to myarray and 1 to the anonymous one?

The docs on the internet suggest that slices in go are exactly like in Rust, a contiguous sequence of array's elements. If so, in my example of inserting (as well as when deleting), there must be a lot happening under the hood.

Inserting elements in to a slice can be done quite easily since the introduction of the slices package to the standard library.

https://pkg.go.dev/slices#Insert

  • You shouldn’t need a library to do this simple operation.

    I’m guessing the go language design went too far into “simplicity” at the expense of reasonableness.

    For example, we can make a “simpler” language by not supporting multiplication, just use addition and write your own!

    • The operation is simple in concept, but can be costly from a compute standpoint when n is large. Multiplication has predicable performance. Insert does not. It being a function indicates that it is doing a lot of things and thus offers pause to make sure that the operation is acceptably within your operational bounds.

      It could have been a builtin function, I suppose, but why not place it in the standard library? It's not a foundational operation. If you look at the implementation, you'll notice it simply rolls up several foundation operations into one function. That is exactly the kind of thing you'd expect to find in a standard library.

> Well, then they look awkward and have give a feel like it's a syntax abuse.

So nothing to worry about?

> how do I insert an element in the middle of an array?

Same as in C. If the array allocation is large enough, you can move the right hand side to the next memory location, and then replace the middle value.

Something like:

    replaceWith := 3
    replaceAt := 2
    array := [5]int{1, 2, 4, 5}
    size := 4
    for i := size; i > replaceAt; i-- {
        array[i] = array[i-1]
    }
    array[replaceAt] = replaceWith
    fmt.Println(array) // Output: [1 2 3 4 5]

If the array is not large enough, well, you are out of luck. Just like C, arrays must be allocated with a fixed size defined at compile time.

> The docs on the internet suggest that slices in go are exactly like in Rust, a contiguous sequence of array's elements.

They're exactly like how you'd implement a slice in C:

    struct slice {
        void *ptr;
        size_t len;
        size_t cap;
    };

The only thing Go really adds, aside from making slice a built-in type, that you wouldn't find in C is the [:] syntax.

Which isn't exactly the same as Rust. Technically, a Rust slice looks something like:

    struct slice {
        void *ptr;
        size_t len;
    };

There is some obvious overlap, of course. It still has to run on the same computer at the end of the day. But there is enough magic in Rust to hide the details that I think that you lose the nuance in that description. Go, on the other hand, picks up the exact same patterns one uses in C. So if you understand how you'd do it in C, you understand how you'd do it in Go.

Of course, that does mean operating a bit lower level than some developers are used to. Go favours making expensive operations obvious so that is a tradeoff it is willing to make, but regardless if it were to make it more familiar to developers coming from the land of magic it stands that it would require more magic, not less.

  • Ok, so mostly we agree. And I was right that you can't just concatenate different slices (e.g. to remove one item from the middle), hence Go has to do a lot of work under the hood to do that. I count this as magic.

    • > Ok, so mostly we agree.

      I don't follow. Information isn't agreeable or disagreeable, it just is.

      > And I was right that you can't just concatenate different slices

      That's right. You would have to physically move the capacitors in your RAM around (while remaining powered!) in order to do that. Given the limits of our current understanding of science, that's impossible.

      > hence Go has to do a lot of work under the hood to do that.

      Do what? You can't actually do that. It cannot be done at the hardware level. There is nothing a programming language can do to enable it.

      All a programming language can do is what we earlier demonstrated for arrays, or as slices allow dynamic allocation, if the original slice is not large enough you can also copy smaller slices into a new slice using a similar technique to the for loop above.

      Go does offer a copy function and an append function that do the same kind of thing as the for loop above so you do not have to write the loop yourself every time. I guess that's what you think is magic? If you are suggesting that calling a function is magic, well, uh... You're not going to like this whole modern programming thing. Even Rust has functions, I'm afraid.

      The Go standard library also provides a function for inserting into the middle of a slice, but, again, that's just a plain old boring function that adds some conditional logic around the use of the append and copy functions. It is really no different to how you'd write the code yourself. So, unless function are still deemed magic...

    • I may have misunderstood "you can't just concatenate different slices (e.g. to remove one item from the middle" but does [0] not do what you're talking about?

      (with the caveat that anything else sharing `a` will be mangled, obvs.)

      [0] https://go.dev/play/p/uQdoa3mUF00