Hello, I'm writing thread::scope
equivalent for threadx, and having some problems with lifetimes. Here is a small example of what it basically looks like. If remove thread
from spawn
arguments, it becomes pretty much the same as in std and it works. But in that state it does not compile. If change &mut thread
to &thread
it will not compile either, but use case is more understandable with &mut
in my opinion, so I left it as it is.
My guess is that borrow checker is thinking that scope captured in closure, that is getting stored in thread, might be still referenced by the end of scope. But even if &mut
is changed to &
and body of Scope::spawn
replaced with todo, error persists - maybe some interior mutability concerns? My guess is that I don't know how to express that I'll clean everything with lifetimes.
Do you have any thoughts how to get stuff like that to compile? I'm assuming it will involve unsafe code to tackle lifetimes, I'm absolutely fine with it - I'll ensure all safety conditions, just need to first express lifetimes right.
use std::marker::PhantomData;
struct Thread<F> {
closure: Option<F>,
}
struct ThreadHandle<'a, F> {
thread: &'a mut Thread<F>,
}
struct ScopeData<'scope> {
data: PhantomData<&'scope ()>,
}
struct Scope<'scope, 'env: 'scope> {
scope_data: ScopeData<'scope>,
scope: PhantomData<&'scope mut &'scope ()>,
env: PhantomData<&'env mut &'env ()>,
}
pub struct ScopedJoinHandle<'scope, 'params, F> {
data: &'scope ScopeData<'scope>,
handle: ThreadHandle<'params, F>,
}
impl<F> Thread<F> {
const fn new() -> Self {
Thread { closure: None }
}
}
impl<'scope, 'env> Scope<'scope, 'env> {
fn spawn<F>(
&'scope self,
thread: &'scope mut Thread<F>,
f: F,
) -> ScopedJoinHandle<'scope, 'scope, F>
where
F: FnOnce() + Send + 'scope,
{
ScopedJoinHandle {
data: &self.scope_data,
handle: spawn(thread, f),
}
}
}
fn spawn<F>(thread: &mut Thread<F>, f: F) -> ThreadHandle<'_, F> {
thread.closure = Some(f);
ThreadHandle { thread }
}
fn scope<'env, F, P: 'static>(mut p: P, f: F)
where
F: for<'scope> FnOnce(&'scope mut P, &'scope Scope<'scope, 'env>),
{
let scope = Scope {
scope_data: ScopeData { data: PhantomData },
scope: PhantomData,
env: PhantomData,
};
f(&mut p, &scope);
drop(p);
}
fn main() {
scope((Thread::new(), Thread::new()), |(t1, t2), s| {
Scope::spawn(
s, //
t1,
|| _ = Scope::spawn(s, t2, || {}),
);
});
}