Let's say I wanted to write a concurrency function that takes N functions as input, calls them concurrently, and waits for all of them to finish. Like std::thread::spawn
, but for when there are a fixed number of functions and I don't need to spawn them incrementally. In theory I think I can accept them like this:
fn fan_out(_fns: &[&dyn Fn()]) {
todo!();
}
However, in practice it seems it's not possible to call this conveniently unless all of the functions have the same type underneath. For example, this doesn't work because each input is not already a &dyn Fn()
:
fan_out(&[
|| println!("taco"),
|| println!("burrito"),
]);
…and then also probably later I'd get a compiler error about how they have different types because of course you can't have an array of disparate types.
Is there any way to structure this so I can have approximately that syntax when calling fan_out
? In C++ for example I'd be able to do the following, since the lambdas implicitly convert to std::function<void()>
:
void FanOut(std::vector<std::function<void()>> fns);
FanOut({
[] { std::cout << "taco\n"; },
[] { std::cout << "burrito\n"; },
});
This code runs just fine:
fn my_fun(fns: &[&dyn Fn()]) {
for f in fns {
f();
}
}
fn main() {
let a = || println!("A");
let b = || println!("B");
my_fun(&[&a, &b]);
}
This works because a
and b
are values with different, unique closure types, and &[&a, &b]
coerces &a
and &b
to &dyn Fn()
.
You could do that explicitly as well:
let arr: [&dyn Fn(); 2] = [&(|| println!("A")), &(|| println!("B"))];
my_fun(&arr);
But when you try to do it directly, you're passing two values with different and unique closure types:
my_fun(&[|| println!("A"), || println!("B")]);
That can't work.
You could also box the closures, if you need a bit more control over the closures in your function:
fn my_fun(fns: &[Box<dyn Fn()>])
{
for f in fns {
f();
}
}
fn main() {
my_fun(&[
Box::new(|| println!("A")),
Box::new(|| println!("B")),
]);
}
This doesn't work because a closure doesn't automatically coerce to &dyn Fn()
. You have to take a reference to it. This isn't even unique to closures, this is common to all &dyn _
types -- impl Foo
never automatically coerces to &dyn Foo
, but &impl Foo
can.
That is, while this does not compile:
fan_out(&[|| println!("taco"), || println!("burrito")]);
This does:
fan_out(&[&|| println!("taco"), &|| println!("burrito")]);
To me this seems to qualify as "approximately that syntax."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With