Live data from Hacker News

Go(lang): Robust generic functions on slices

go.dev

21–30 of 85 posts

Re: Go(lang): Robust generic functions on slices

#21
post #7

The problems with the API they point out are almost all things that rust's ownership system was built to solve. Things like: slices.Sort(s) // correct slices.Compact(s) // incorrect slices.Delete(s, ...) // incorrect s := slices.Delete(s, ...) // incorrect if 's' is referenced again in the outer scope s = slices.Delete(s, ...) // correct All of those are solved by having functions like 'slices.Sort' take a '&mut' ref…

You don't even need a notion of ownership. A distinction between an immutable and mutable slice should be enough, because an immutable slice can never be changed which implies that its excess capacity (if any) can't be exploited for optimization.

You'd need three types: immutable slice of an immutable array, mutable slice of a mutable array (ie basically a reference to a mutable array), and an immutable slice of a mutable array.

Re: Go(lang): Robust generic functions on slices

#22
post #13

Earlier quoted context omitted.

This would not allow the previous errors to be checked by the compiler since the main thing you're relying on is the name. Nothing prevents you to call deleted(mutable) and discard the result apart from the name.

While that's a valid concern, it is an orthogoal issue as it can be similarly replicated in Rust as well. Rust references always track mutability but we can sidestep that by using `std::borrow::Cow`: fn compacted (input: Cow ) -> Cow { ... } Then it is clear that, for example, `compacted(vec![...].into());` as a statement will exhibit the same behavior because `Cow` doesn't have `#[must_use]`. Rust avoids this issue…

must_use doesn’t have to be on a type, it can be applied to a function

Re: Go(lang): Robust generic functions on slices

#23
post #2

A missing tidbit that may help contextualize this post: One of the things about Go that surprised me is that if you have a slice which does not represent the full capacity of the underlying array, you can go ahead and reslice it up to that full capacity even though it's a panic to access the things you're reslicing directly: https://go.dev/play/p/oThz2bNFwgr Consequently, the GC has to assume that anything forward of…

One detail in the latest 1.22 release is a change in the "slices" library [1] (the library is based on generics, introduced in 1.21) that helps avoid such cases: > Functions that shrink the size of a slice (Delete, DeleteFunc, Compact, CompactFunc, and Replace) now zero the elements between the new length and the old length. [1]: https://tip.golang.org/doc/go1.22#slices [2]: https://pkg.go.dev/slices

[deleted]

Re: Go(lang): Robust generic functions on slices

#25
post #13

Earlier quoted context omitted.

This would not allow the previous errors to be checked by the compiler since the main thing you're relying on is the name. Nothing prevents you to call deleted(mutable) and discard the result apart from the name.

> Nothing prevents you to call deleted(mutable) and discard the result The Go compiler generates an error when you are (silently) ignoring the return value of any function. Or, to put it in other words, every compiler which does allow to (silently) ignore the return value of a function, should not be used at all (C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always)…

[deleted]

Re: Go(lang): Robust generic functions on slices

#26
post #13

Earlier quoted context omitted.

This would not allow the previous errors to be checked by the compiler since the main thing you're relying on is the name. Nothing prevents you to call deleted(mutable) and discard the result apart from the name.

> Nothing prevents you to call deleted(mutable) and discard the result The Go compiler generates an error when you are (silently) ignoring the return value of any function. Or, to put it in other words, every compiler which does allow to (silently) ignore the return value of a function, should not be used at all (C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always)…

> The Go compiler generates an error when you are (silently) ignoring the return value of any function.

It does not. You can actually infer that from TFA listing cases as problematic which would be caught by such a compilation error, and confirming it by just compiling them:

    a := []int{}
    // compiler says nothing
    slices.Delete(a, 0, 0)
The builtins are special cased to error if their return value is ignored (except for copy and recover).

> C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always

You can't even mark your own functions or types as nodiscard in Go. You need third-party tooling even just to ensure you're not unwittingly ignoring error results:

    f, err := os.Create("/tmp/filename")
    if err == nil {
        return
    }
    // compiler doesn't say anything even though f.Close returns error
    f.Close()

Re: Go(lang): Robust generic functions on slices

#28

> func Index[S ~[]E, E comparable](s S, v E) int { After seeing this signature, I think that Go is giving up on it's simpleness principle.

I was quite upset that they introduced generics. It is slow marching into C++ like look and feel and all the eyesore that it entails.

Re: Go(lang): Robust generic functions on slices

#29

Earlier quoted context omitted.

> Nothing prevents you to call deleted(mutable) and discard the result The Go compiler generates an error when you are (silently) ignoring the return value of any function. Or, to put it in other words, every compiler which does allow to (silently) ignore the return value of a function, should not be used at all (C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always)…

> The Go compiler generates an error when you are (silently) ignoring the return value of any function. It does not. You can actually infer that from TFA listing cases as problematic which would be caught by such a compilation error, and confirming it by just compiling them: a := []int{} // compiler says nothing slices.Delete(a, 0, 0) The builtins are special cased to error if their return value is ignored (except fo…

Sorry, yes, you are of course right. That's the linter which complains, not the compiler.

I have to say that I don't understand the rationale of a compiler that errors on unused variables but lets the user silently ignore function return values. As a solution to explicitly ignore return values already exists in the language.

Re: Go(lang): Robust generic functions on slices

#30
post #7

The problems with the API they point out are almost all things that rust's ownership system was built to solve. Things like: slices.Sort(s) // correct slices.Compact(s) // incorrect slices.Delete(s, ...) // incorrect s := slices.Delete(s, ...) // incorrect if 's' is referenced again in the outer scope s = slices.Delete(s, ...) // correct All of those are solved by having functions like 'slices.Sort' take a '&mut' ref…

IMHO, all these comes from the million dollar mistake that Go made from the very beginning, with slices being a bastard between both an owned and non-owned data container. Indeed, any function accepting a slice may simply use it as an immutable view on whatever data it needs, or modify it, potentially re-alloc it, and wreak havoc by invalidating all previous references to it and/or its elements. And God forbid you passed a subslice to such a function, you're in for a nasty surprise.

Even without going the whole 9 yards of the Rust memory model, something like the views in C++, or the java {Array,Linked}Lists containing references to objects and thus being resistant to re-alloc, or even plainly immutable list like in OCaml are all working and simple enough solutions to the issue.

I still can't wrap my mind around how supposedly experienced people like Pike & co. may have designed slices, then went ‶yep, that makes perfect sense for a robust, basic, widely used data structure to create our language around″, outside of a few toy programs.

Post reply on HN