7 ms·
From a distance it looks difficult to write in. For example, two recommended ways to delete from a slice: a = append(a[:i], a[i+1:]...) // or a = a
by ridiculous_fish 5y ago
From a distance it looks difficult to write in. For example, two recommended ways to delete from a slice:
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
Both seem harder than necessary.
Is that code just idiomatic, and Go programmers recognize it instantly? Or maybe they don't deal with slices that often?
https://github.com/golang/go/wiki/SliceTricks https://github.com/golang/go/wiki/SliceTricks
- yobert 5y agoI use slices all the time, but very rarely need to delete out of the middle of them. So it seems like a bit of an edge case to me.
- barsonme 5y agoNo, you deal with slices all the time. You just recognize the idiom. You can restructure it if you don't need to preserve order: a[i] = a[len(a)-1] a = a[:len(a)-1] In general* Go does not hide complexity from you. It's a blessing and a curse. *: because the Internet is a nitpicky place.
- benhoyt 5y agoWith the introduction of generics in Go 1.18 and the accompanying "slices" package (https://github.com/golang/go/issues/45955 https://github.com/golang/go/issues/45955), we might soon write that as: a = slices.Delete(a, i, i+1) That said, in code I write, I rarely need to do an in-place delete from a slice. I think it's rare enough that recognizing the idiom is okay.
- azth 5y agoIt's so weird why they're adding a slices package instead of making them functions on the slice itself. They did the same with strings. Compare strings.ToUpper(strings.Replace(strings.Trim(s), "a", "b"))) Instead of s.Trim().Replace("a, "b").ToUpper()
- benhoyt 5y agoFor better or worse, this is by design. See: https://golang.org/doc/faq#methods_on_basics https://golang.org/doc/faq#methods_on_basics It's "to avoid complicating questions about the interface (in the Go type sense) of basic types". It also allows separating the builtin functions, of which there are very few (they have to be in the core language spec), from the stdlib functions like strings.ToUpper, which there are many many more of and are added to more quickly than the builtins.
- azth 5y agoI'm not sure what their answer means. They used a similar hand wavy answer about why the language has null pointers. "Complication" here refers to how complicated their implementation of the compiler is, not the end user complication, which is what they traded off (simpler compiler for more complex user code). I didn't get the last point. builtin functions are there (for a big part) because the language doesn't have generics. Functions on types can be added without affecting the interface, since strings don't implement any interface (at least not on purpose, another problem with golang). Java constantly enriches standard library types with more useful methods.
- lanstin 5y agoBecause in-place delete is slow and you want to avoid algorithms with a lot of in-place deletes. So it's ok to have it be awkard, it's like the names of rare elements being longer than the names of common elements, not an issue.
- jdsleppy 5y agoWith two years of working in Go at two companies, I have never had to write that. I can't remember writing the equivalent in Python over even more years, either. The 99% case is building a list/slice, not removing from one. Have you ever had to remove elements from a list/slice?
- ridiculous_fish 5y agoYes, a sorted vector is a common data structure for frequent queries and infrequent updates.
- kjksf 5y agoYou can find things to complain in any language, including the languages that YOU use. For every complain like that in Go I'll find a hundred in C++, ten in Java / JavaScript / Python. As to your question, as a full-time Go programmer: this doesn't come up often. When it does, I google "slice tricks" and copy & paste the formula. It'll be fixed by next release because generics will enable writing a function that does that and that function will be in standard library. Why wasn't this fixed before generics? Because the only way to do it would be via a compiler built-in function and Go team is rightfully reluctant to bloat the language / compiler for minor functionality like that.
- ridiculous_fish 5y agoOof, I have no desire to spark a language war. Every language has its warts for sure. It seems strange to not provide slice.erase as a primitive operation, and instead to recommend implementing it in terms of append. Usually removing from a vector does not require allocation. Still it may be for the best with upcoming generics, fewer builtins is good.
- woodruffw 5y agoThis has been my experience as well: I had to fix a bug in a Go codebase recently, and the fix required me to de-duplicate an unordered collection of elements. It turns out that there's (1) no built-in way to do this, and (2) there isn't even a built-in set structure in Go, so you can't do the obvious solution without explicitly using a map with a chaff value. Stack Overflow has dozens of duplicate questions for this, all with ridiculous O(n^2) or buggy (or both) answers.
- jrockway 5y agomap[T]struct{} is the set type in Go. (Note that struct{} is represented with 0 bytes of memory, so it's not chaff.) You might like the slices proposal for utility functions like these: https://github.com/golang/go/issues/45955 https://github.com/golang/go/issues/45955
- woodruffw 5y agoThanks. I wasn't aware that struct{} is zero-sized, so that plugs that hole. I'll admit freely that I'm not a proficient Go programmer, so it's easier for me to see the things I don't like than the things that Gophers praise. But even this solution leaves me unsatisfied, in contrast to what I'd reach for in Rust or even C++: it requires that I know that struct{} is zero-sized, and I still have to do the manual legwork of writing a CS101 dedupe function.
- tialaramex 5y agoZero Size optimisations are a thing in Rust too though, and I don't think an empty struct being zero size is any more surprising than Rust's empty tuple being zero size. Like, if you tell Rust you want 4 million empty tuples in a vector, it will give you a vector with exactly 4 million empty tuples in it and no heap allocation because those empty tuples don't take space, so, 4 million of them also doesn't take up any space.
- woodruffw 5y ago
- jrockway 5y agogopls seems to be integrating some of the "tricks" into editor completion. This one isn't there, but other idioms are. (With gopls > 0.7.0, it's enabled by default. Details: https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.0 https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.0)
- avl999 5y agoDealing with any kind of collections will often require you to write more code than you are used to, see the other post in this thread about someone complaining about a lack of Set type. Most of these problems are due to the fact that the language doesn't have generics yet, however generics are coming soon and once they do arrive we will have a robust collection and concurrent collection library to alleviate for most of these problems. For now you just have to accept these quirks as part of the tradeoff of working with the language. I don't find it that big of a deal esp with the solution on the horizon.
- bilinguliar 5y agoIt is not only hard for you, but it is also hard for a computer. And Go expresses this perfectly. Once you hide this behind a convenient `slice.Delete(x)`, you may end up with a codebase that bleeds performance.
- _ph_ 5y agoThis is an interesting point. On the first glance, it looks like a very obvious missing piece and having to hand-write such expressions feels like unnecessary painful. On the second glance, one can see why probably it was left out: there are two fundamental different ways to implement the deletion. You can copy the last element into the slot of the deleted element, or you can move all elements after the deleted one place to the left. The latter is what you implemented and preserves the ordering, which often is desirable. The former is way more efficient, if you don't need to preserve the order. A default implementation would probably choose to preserve the order and thus introduce a systematic inefficiency which is easy to be avoided. Fortunately, with the introduction of generics and especially the new slice package, this discussion becomes moot.