I have a struct Job
that contains an enum JobType
. The enum variants contain closures -- simplified (C
is really a generic, but it's not important):
struct OneShotData {
task: Box<dyn FnOnce(&C) -> JobResult + Send>
}
struct RecurringData {
task: Box<dyn Fn(&C) -> JobResult + Send>
}
enum JobType {
OneShot(OneShotData),
Recurring(RecurringData)
}
struct Job {
jinfo: JobType
}
(The recurring bit is new; earlier only one-shot jobs were supported).
As implied by their names, OneShot is run once (note FnOnce
) while Recurring can run more than once (note Fn
).
Jobs are stored in a HashSet
, and when a Job
is run it is first removed from the HashSet
, then its task
is run on a separate thread, which does something like this:
let res = match job.jinfo {
JobType::OneShot(os) => (os.task)(&ctx),
JobType::Recurring(ref r) => (r.task)(&ctx)
};
The issue is that for the Recurring
case, I want to put job
back into the HashSet
whence it came, but clearly it can't because a partial move happened. If I make it a match &job.jinfo
, then the OneShot
case won't work, since its os.task
is an FnOnce
.
Reworking this to using if let
solves one problem, but the borrow-checker is (understandably) unaware that restore_it
is intended to imply that no partial borrows have happened:
let mut restore_it = false;
let res = if let JobType::OneShot(os) = job.jinfo {
(os.task)(&mgr, &sh.ctx)
} else if let JobType::Recurring(r) = &job.jinfo {
restore_it = true;
(r.task)(&mgr, &sh.ctx)
} else {
panic!("Unhandled");
};
if restore_it {
idle_jobs.insert(job);
}
How can I handle both these cases? For the OneShot
I want to consume the FnOnce
, so I don't care that partial moves happened. For the Recurring
I don't want any partial moves, because I want to store it [the job
object] back into the HashSet
.