Now I have foo function, which aims to use multiple threads to handle something_heavy, and will not return until those threads are finished (by calling join(), e.g.).
I think the whole logic is completely safe, since something_heavy is an immutable reference, so there would be no race condition; threads are waited for finishing, which would not lead to use-after-free of something_heavy.
Now, what is the most preferred way to implement this logic? I know I can clone SomethingHeavy to bypass those reference-checking rules, but it may have high costs; and since there is no race condition, Mutex or Arc may be unnecessary.
When working on some code that was doing parallel computations, I found the rayon crate more powerful than crossbeam. Correct me if I'm wrong, but I think crossbeam spawns an OS thread for each call of crossbeam::scope, while rayon works with a thread pool and thus decides on its own how many actual OS threads to spawn for a parallel computation. Therefore, you can use rayon to perform parallel computations on a Vec with thousands of elements without actually spawning thousands of OS threads.
Yeah, the two work at different levels of abstraction. Rayon is all about doing jobs in parallel, where you don't actually care which thread your code is being run on (so they can use a thread pool), whereas crossbeam gives you control over threads and lets you pass references from the current thread's stack to another thread.
As a side note, there used to be a similar API to crossbeam::scope in the standard library which used an RAII guard to make sure spawned threads can't access variables after the current thread exits but that strategy was found to be unsound. The issue actually turned into a bit of a watershed moment for Rust's safety guarantees.