Remix.run Logo
simonask 3 days ago

This is an interesting comment, because you point directly at the exact reason Rust’s approach is so productive.

In the C/C++/Zig code, you would add the second concurrent access, and then start fixing things up and restructuring things - if you, the programmer, knew about the first access, and knew that the concurrent access is a problem.

In countless cases, that work would not be done, and I cannot blame any of the involved people, because managing that kind of detailed complexity over the lifespan of a project is not humanly possible. The result is another concurrency bug, meaning UB in production.

Having the compiler tell you about such problems up front, exactly when they happen, is a complete game changer.

ajross 3 days ago | parent [-]

> In the C/C++/Zig code, you would add the second concurrent access, and then start fixing things up and restructuring things

Well, sure, which in practice means throwing a lock around it.

I mean, I get it. There's a category of bugs that happen in real code. Rust chooses some categories of bugs (certainly not all of them) to construct walls around and forces code into idioms that can be provably correct for at least a subset[1] of the bug space. For the case of memory safety, that's a really pretty convincing case. Other areas are less clear; in particular I'm not a fan of Rust's idea of threadsafety and don't think it fits what actually performance-parallel code needs.

[1] You can 100% write racy code with Sync/Send! You can write racy code with boring filesystem access using 100% safe rust (or 1980's csh code, whatever), too. Race conditions are inherent to concurrency. Sync/Send just protect memory access, they do nothing to address semantic/state bugs.

simonask 2 days ago | parent [-]

This is true out of the box - Rust isn't magical, and it can't fix all your bugs for you. But it does make its own tools available to you, an API designer. Lifetimes are available to you without ever making any actual references, and the Send/Sync traits are available to you without constructing any standard synchronization mechanism.

You can construct something like `PhantomData<&mut ()>` to express invariants like "while this type exists, these other operations are unavailable". You can implement Send and/or Sync to say things like "under these specific conditions, this thing is thread safe". These are really powerful features of the type system, and no other mainstream language can really express such invariants at compile time.

ajross 2 days ago | parent [-]

Again though, I don't see a lot of value there. You seem to be implicitly repeating the prior that rust prevents race conditions, and it emphatically does not. It detects and prevents concurrent unlocked[1] access to a single memory location, which is not the same thing. Real races are much more complicated than that and happen in the semantic space of the application, not the memory details (again, think of email lockfiles as a classic race that has nothing to do with memory at all).

[1] Though with overhead. It's tends not to be possible in safe rust to get it to generate code that looks like a pthread_mutex critical section. This again is one of my peeves, you do dangerous shared memory concurrency for performance!