3 ms·
> You can't even access the data without locking the mutex. It's even nicer than that: you can actually access data without locking the mutex, because while yo
by Nauxuron 11mo ago
> You can't even access the data without locking the mutex.
It's even nicer than that: you can actually access data without locking the mutex, because while you hold a mutable borrow to the mutex, Rust statically guarantees that no one else can acquire locks on the mutex.
https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.get_mut https://doc.rust-lang.org/std/sync/struct.Mutex.html#method....
- jstimpfle 11mo agoGiven 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.
- dwattttt 11mo agoThe 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.
- jstimpfle 11mo agoDo 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.
- adwn 11mo agoWhen 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
- jstimpfle 11mo agoInitialization is always special. A mutex can't protect that which doesn't exist yet. The right way to initialize your object would be to construct the message first, then construct the composite type that combines the message with a mutex. This doesn't require locking a mutex, even without any borrow checker or other cleverness.
- adwn 11mo agoDude, it's a simplified example, of course you can poke holes into it. Here, let me help you fill in the gaps: let mut object = prepare_generic_entry(general_settings); let mutable_msg = object.msg.get_mut(); do_specific_message_modification(mutable_msg, special_settings); The point is, that there are situations where you have exclusive access to a mutex, and in those situations you can safely access the protected data without having to lock the mutex.
- jstimpfle 11mo agoSorry, I don't find that convincing but rather construed. This still seems like "constructor" type code, so the final object is not ready and locking should not happen before all the protected fields are constructed. There may be other situations where you have an object in a specific state that makes it effectively owned by a thread, which might make it possible to forgo locking it. These are all very ad-hoc situations, most of them would surely be very hard to model using the borrow checker, and avoiding a lock would most likely not be worth the hassle anyway. Not sure how this can help me reduce complexity or improve performance of my software.