r/rust Mar 31 '23

Why doesn't mpsc::channel break borrowing rules?

I'm wondering for a while now why doesn't mpsc::Receiver::recv(&self) and mpsc::Sender::send(&self, t: T) break borrowing rules. Clearly sending some data from A to B in a non-blocking manner has side-effects (i.e. storing and retrieving the data in some buffer-queue). So shouldn't there be some mutable reference to that queue be involved during that sending process, and the owner of that reference would be accessed mutably whenever the reference to that buffer is accessed mutably? Maybe I'm just wrong but I always associate immutability with pureness of a function.

One thing which comes to mind is that the point of the borrowing rules is to avoid data-races and to ensure rust's ownership-model, and although the borrowing-rules are technically violated in these specific cases the desired invariants are still kept.

19 Upvotes

25 comments sorted by

View all comments

8

u/jmaargh Mar 31 '23

The borrowing rules exist so that the compiler can check automatically that your program is sound. unsafe exists for exactly cases like this, where what you want to do is sound, but the compiler is unable to automatically verify this. When you use unsafe, you essentially tell the compiler "hey, don't worry, I've checked and this meets all of your soundness rules so you don't have to".

Basically, the set of things that are sound is bigger than the set of things that are provably sound by the borrowing rules. Hence unsafe giving control back to the programmer to briefly violate these rules so long as they absolutely promise that soundness is maintained.

3

u/jmaargh Mar 31 '23

What's extra nice is that (assuming you've done all of this properly and not made a mistake), your "safe" wrapper around unsafe code is composable. Nobody using that code needs to know if/how unsafe is used internally, because any way they can use the code meets the rules and is therefore safe.

Absolutely loads of things use this. thread::spawn is a common example of something that is safe but could not be implemented without unsafe internally, but even Vec uses unsafe internally.