4 ms·
I love Go and it is my preferred language. But the pointer thing can be very confusing when it comes to slices. Idiomatic go is to generally use slices, not arr
by abtinf 10y ago
I love Go and it is my preferred language. But the pointer thing can be very confusing when it comes to slices. Idiomatic go is to generally use slices, not arrays. Slices are passed by copy, but contain a reference to the underlying array.
In other words, the default is pass by copy, but the fundamental idiomatic data structure is effectively pass by reference. This makes it way too easy to accidently pass parameters by reference when you didn't intend to or, even worse, try to manipulate the same array from multiple goroutines without proper locking.
This bit me hard in an early system I wrote in Go. The situation is marginally improved with the panic-on-multiple-thread-manipulating-slice feature. The only way to deal with it is to be paranoid about any code that passes slices, which weakens one of my favorite things about Go: you can look at the code, instantly understand what it is doing, and trust there is no magic.
Edit: to be clear, I think pointers should be included in Go. I would just prefer that slices be handled differently - perhaps by treating them as a special case, where the compiler passes the underlying array by copy unless explicitly told to pass by reference.
- Animats 10y agoThis makes it way too easy to accidently pass parameters by reference when you didn't intend to or, even worse, try to manipulate the same array from multiple goroutines without proper locking. That's the biggest hole in Go's "share by communicating" story. In practice, Go channels are just a built-in queue object, with the same concurrency problems as queue objects in other languages. Rust has a borrow checker, so if you pass a reference through a queue, the compiler notices. Gp strings are read-only, so slices of strings, although sharing data, are not sharing mutable data. That tends to make the problem at least manageable.
- yandrypozo 10y agoThat's a common mistake, don't look at channels as queues, one thing is be buffered and other one it's working as a queue, actually buffered channels should be avoided at least you really need a buffer.
- Animats 10y agoHuh?
- thethirdone 10y agoChannels can be made with no buffering at all. In this case trying to send without a receiver is blocking, so the channel is not really acting as a queue anymore. Buffering is only an option to improve performance. Programs should (if they are correct in a sense) be able to work with channels without buffering.
- agentgt 10y ago> Channels can be made with no buffering at all. In this case trying to send without a receiver is blocking, so the channel is not really acting as a queue anymore. It depends on how you define a queue. That is in this case whether there is blocking or not. In Java this distinction is made clear with BlockingQueues and plain Queues. A non buffering blocking queue in Java is a SynchronousQueue or casually known as a hand off queue or rendezvous queue. Unfortunately unlike Go these queues are painfully expensive in Java because Java doesn't have green threads as well I believe the queues use expensive locks (then again locks can be at times more efficient than spinning/sleeping the cpu aka ConcurrentLinkedQueue but in general they are expensive). Going back to the ambiguity of what is a queue it gets even more complicated when talking about distributed queues/streams where you have acknowledgements, prefetching, round robin distribution etc (ie AMQP).