Automatically Boxing Function Arguments

Hey everyone, first time poster. I’m writing my own simple thread pool that adds jobs as closures to a queue and then executes them across any number of threads synchronously.

I got this to work defining a Job struct that stores the type:

Box<Fn() + Send + Sync>

It works, but a user still has to explicitly type out something like this:

Job::new( Box::new ( move || { 
    //blah blah 
} ) );

I want to eliminate the necessity to explicitly “Box” the closure, like what I imagine thread::spawn() does - but maybe I’m wrong on that. Is there some trick to allow my new() function to automatically convert this closure argument into a boxed closure behind the scenes?

Here’s what I’m working with so far:

type Fp = Box<Fn() + Send + Sync>;
pub struct Job {
    fp: Fp,
}
impl Job {
    pub fn new(fp: Fp) -> Job {
        Job { fp }
    }

Without the explicit boxing, I’m currently getting compiler errors about unknown size at compile time for the closure type.

Thanks for your help.

You could box it yourself in your new function.

type Fp = Box<Fn() + Send + Sync>;
pub struct Job { fp: Fp, }
impl Job {
    pub fn new<F: Fn() + Send + Sync>(fp: F) -> Job {
        Job { fp: Box::new(fp) }
    }
}
1 Like

I think the main problem here is that you’re using trait objects without really knowing, or maybe you did but haven’t considered the possibility of using a generic and then boxing it like @KrishnaSannasi does in their post. In the future, please use the dyn keyword to denote trait objects, like so:

struct Job {
    job_func: Box<dyn Fn() + Send + Sync>
}

You could also not use an explicitly generic type, and instead use an impl trait type, like so:

pub fn new(x: impl Fn()) -> Job {
    Job { fp: Box::new(x) }
}
2 Likes

@KrishnaSannasi Thanks for your answer! Using your suggestion of a generic that is then boxed in the new function, I got it to work as I was hoping to! I was messing around with boxing it in the new function, but it was the declaration of a generic type that I was missing, and it now compiles as long as I add the 'static lifetime specification like so

pub fn new<F: Fn() + Send + Sync + 'static>(fp: F) -> Job

Is this consistent with your understanding? Lifetimes remain a challenge for me in most cases…

@OptimisticPeach Thank you for your explanation and another option. I was most certainly using trait objects without knowing it, but I can see that now and everything about it makes a lot more sense. Thanks!

Thank you both for your quick and helpful answers!

1 Like

Yeah, 'static is necessary for threads unless you used scoped threads, like from rayon or crossbeam.