← Back to context

Comment by jstimpfle

10 hours ago

Given a data item of non-thread safe type (i.e. not Mutex<T> etc), the borrow checker checks that there's only ever one mutable reference to it. This doesn't solve concurrency as it prevents multiple threads from even having the ability to access that data.

Mutex is for where you have that ability, and ensures at runtime that accesses get serialized.

The maybe unexpected point is that if you know you're the only one who has a reference to a Mutex (i.e. you have a &mut), you don't need to bother lock it; if no one else knows about the Mutex, there's no one else who could lock it. It comes up when you're setting things up and haven't shared the Mutex yet.

This means no atomic operations or syscalls or what have you.

  • Do you have an example? I don't program in Rust, but I imagine I'd rarely get into that situation. Either my variable is a local (in a function) in which case I can tell pretty easily whether I'm the only one accessing it. Or, the data is linked globally in a data structure and the only way to access it safely is by knowing exactly what you're doing and what the other threads are doing. How is Rust going to help here? I imagine it's only making the optimal thing harder to achieve.

    I can see that there are some cases where you have heap-data that is only visible in the current thread, and the borrow checker might be able to see that. But I can imagine that there are at least as many cases where it would only get in the way and probably nudge me towards unnecessary ceremony, including run-time overhead.

    • When you construct an object containing a mutex, you have exclusive access to it, so you can initialize it without locking the mutex. When you're done, you publish/share the object, thereby losing exclusive access.

          struct Entry {
              msg: Mutex<String>,
          }
          ...
          // Construct a new object on the stack:
          let mut object = Entry { msg: Mutex::new(String::new()) };
          // Exclusive access, so no locking needed here:
          let mutable_msg = object.msg.get_mut();
          format_message(mutable_msg, ...);
          ...
          // Publish the object by moving it somewhere else, possibly on the heap:
          global_data.add_entry(object);
          // From now on, accessing the msg field would require locking the mutex

      3 replies →

    • >I don't program in Rust, but I imagine I'd rarely get into that situation.

      Are you sure? Isn't having data be local to a thread the most common situation, with data sharing being the exception?

      >Or, the data is linked globally in a data structure and the only way to access it safely is by knowing exactly what you're doing and what the other threads are doing.

      That's exactly what the borrow checker does. It tracks how many mutable references you have to your data structure at compile time. This means you can be sure what is local and what is shared.

      Meanwhile without the borrow checker you always have to assume there is a remote probability that your mental model is wrong and that everything goes wrong anyways. That's mentally exhausting. If something goes wrong, it is better to only have to check the places where you know things can go wrong, rather than the entire code base.

      1 reply →