Coming from a C++ background, something that always seemed odd to me about rust was the default behavior of rust's threads being detachment rather than a join. Despite rust obviously enforcing much more safety standards than C++, it allows implicit detachment of threads. For example, this code in C++:
#include <thread>
#include <cstdio>
int main() {
auto t1 = std::thread([]{
int a = 3;
for(;a < 5;++a) {
std::puts("Hello World!");
}
});
return 0;
}
Results in a crash because the thread is not joined nor detached. But the same code on rust is perfectly valid
use std::thread;
fn main() {
let t1 = thread::spawn(|| {
println!("Hello!");
});
}
The reason I bring this up is:
- If desired, there's no way to enforce threads have been joined before the program exits, as in, no equivalent of lifetimes for threads.
- A thread should be able to borrow a non static reference if it is guaranteed to be joined before the value behind the reference goes out of scope.
For 2, consider that rust already does not allow moves if a borrow exists in the current scope, such as
fn main() {
let a = String::from("Hello World!");
let b = &a;
drop(a);
let c = *b;
}
results in
error[E0505]: cannot move out of `a` because it is borrowed
--> src/main.rs:30:9
|
29 | let b = &a;
| -- borrow of `a` occurs here
30 | drop(a);
| ^ move out of `a` occurs here
31 | let c = *b;
| -- borrow later used here
So the idea of a non-detachable thread would be a new type of thread such that a thread would either be joined on drop, or must be joined before the end of it's scope it was spawned in.
fn foobar() {
let a = String::from("Hello World!");
let t1 = thread_non_detachable::spawn(|| {
println!("Using a message {:?}", a);
})
// t1 does not live beyond this scope, it either is joined,
// or joined on drop
}