←back to thread

Go channels are bad

(www.jtolds.com)
298 points jtolds | 1 comments | | HN request time: 0s | source
Show context
hacknat ◴[] No.11211002[source]
I think I've just come to accept that sychronization is the pain point in any language. It's callbacks, promises, and the single event loop in nodejs. It's channels in golang.

No one can come up with a single abstraction for synchronization without it failing in some regard. I code in go quite a bit and I just try to avoid synchronization like the plague. Are there gripes I have with the language? Sure, CS theory states that a thread safe hash table can perform just about as well as a none-thread safe, so why don't we have one in go? However...

Coming up with a valid case where a language's synchronization primitive fails and then flaming it as an anti-pattern (for the clicks and the attention, I presume) is trolling and stupid.

replies(3): >>11211077 #>>11211292 #>>11211863 #
yetihehe ◴[] No.11211077[source]
> No one can come up with a single abstraction for synchronization without it failing in some regard.

Erlang did. Or at least it's as close as possible.

replies(2): >>11211145 #>>11211260 #
hacknat ◴[] No.11211145[source]
I'm not saying Erlang isn't great, but if you need to pass a large datastructure around between Erlang processes then copy message passing starts to be a lot and you need to share memory. You can do it in Erlang, but I'd hardly call it great, and you're avoiding the sync primitive that Erlang offers.
replies(2): >>11211256 #>>11211361 #
catnaroek ◴[] No.11211256[source]
How about Rust's “share by transferring ownership”?

(0) In the general case, whatever object you give to a third party, you don't own anymore. And the type checker enforces this.

(1) Unless the object's type supports shallow copying, in which case, you get to keep a usable copy after the move.

(2) If the object's type doesn't support shallow copying, but supports deep cloning, you can also keep a copy [well, clone], but only if you explicitly request it.

This ensures that communication is always safe, and never more expensive than it needs to be.

---

Sorry, I can't post a proper reply because I'm “submitting too fast”, so I'll reply here...

The solution consists of multiple steps:

(0) Wrap the resource in a RWLock [read-write lock: http://doc.rust-lang.org/std/sync/struct.RwLock.html], which can be either locked by multiple readers or by a single writer.

(1) The RWLock itself can't be cloned, so wrap it in an Arc [atomically reference-counted pointer: http://doc.rust-lang.org/std/sync/struct.Arc.html], which can be cloned.

(2) Clone and send to as many parties as you wish.

---

I still can't post a proper reply, so...

Rust's ownership and borrowing system is precisely what makes RWLock and Arc work correctly.

replies(1): >>11211307 #
hacknat ◴[] No.11211307[source]
What if you want multiple readers at once, and a writer thrown in once in a while?

Edit:

Okay, my point was that the sync primitives of most languages alone can't save you and you're using RWLock in your example, so clearly ownership by itself doesn't solve everything, right? That's the point I'm trying to make.

Edit2:

Hmm, I'll have to check that out. I don't know that I would call Rust's ownership model super easy to reason about, but it is nice that the compiler prevents you from doing so much stupid $#^&.

replies(2): >>11211466 #>>11213916 #
1. pcwalton ◴[] No.11211466[source]
> Okay, my point was that the sync primitives of most languages alone can't save you and you're using RWLock in your example, so clearly ownership by itself doesn't solve everything, right?

The thing is that Rust ensures that you take the locks properly. It's an compile-time error to forget to take the lock or to forget to release the lock†. You can't access the guarded data without doing that.

† For lock release, it's technically possible to hold onto a lock forever by intentionally creating cycles and leaking, but you really have to go out of your way to do so and it never happens in practice.